3 using namespace ARDOUR;
8 /* overloaded operator* that avoids floating point math when multiplying a superclock position by a number of quarter notes */
9 superclock_t operator*(superclock_t sc, Evoral::Beats const & b) { return (sc * ((b.get_beats() * Evoral::Beats::PPQN) + b.get_ticks())) / Evoral::Beats::PPQN; }
12 Meter::bbt_add (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & add) const
14 int32_t bars = bbt.bars;
15 int32_t beats = bbt.beats;
16 int32_t ticks = bbt.ticks;
18 if ((bars ^ add.bars) < 0) {
19 /* signed-ness varies */
20 if (abs(add.bars) >= abs(bars)) {
21 /* addition will change which side of "zero" the answer is on;
22 adjust bbt.bars towards zero to deal with "unusual" BBT math
32 if ((beats ^ add.beats) < 0) {
33 /* signed-ness varies */
34 if (abs (add.beats) >= abs (beats)) {
35 /* adjust bbt.beats towards zero to deal with "unusual" BBT math */
44 Timecode::BBT_Offset r (bars + add.bars, beats + add.beats, ticks + add.ticks);
46 if (r.ticks >= Evoral::Beats::PPQN) {
47 r.beats += r.ticks / Evoral::Beats::PPQN;
48 r.ticks %= Evoral::Beats::PPQN;
51 if (r.beats > _divisions_per_bar) {
52 r.bars += r.beats / _divisions_per_bar;
53 r.beats %= _divisions_per_bar;
64 return Timecode::BBT_Time (r.bars, r.beats, r.ticks);
68 Meter::bbt_subtract (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & sub) const
70 int32_t bars = bbt.bars;
71 int32_t beats = bbt.beats;
72 int32_t ticks = bbt.ticks;
74 if ((bars ^ sub.bars) < 0) {
75 /* signed-ness varies */
76 if (abs (sub.bars) >= abs (bars)) {
77 /* adjust bbt.bars towards zero to deal with "unusual" BBT math */
86 if ((beats ^ sub.beats) < 0) {
87 /* signed-ness varies */
88 if (abs (sub.beats) >= abs (beats)) {
89 /* adjust bbt.beats towards zero to deal with "unusual" BBT math */
98 Timecode::BBT_Offset r (bars - sub.bars, beats - sub.beats, ticks - sub.ticks);
101 r.beats -= 1 - (r.ticks / Evoral::Beats::PPQN);
102 r.ticks = Evoral::Beats::PPQN + (r.ticks % Evoral::Beats::PPQN);
106 r.bars -= 1 - (r.beats / _divisions_per_bar);
107 r.beats = _divisions_per_bar + (r.beats % _divisions_per_bar);
118 return Timecode::BBT_Time (r.bars, r.beats, r.ticks);
122 Meter::bbt_delta (Timecode::BBT_Time const & a, Timecode::BBT_Time const & b) const
124 return Timecode::BBT_Offset (a.bars - b.bars, a.beats - b.beats, a.ticks - b.ticks);
128 Meter::round_up_to_bar (Timecode::BBT_Time const & bbt) const
130 Timecode::BBT_Time b = bbt.round_up_to_beat ();
139 Meter::to_quarters (Timecode::BBT_Offset const & offset) const
143 b += (offset.bars * _divisions_per_bar * 4) / _note_value;
144 b += (offset.beats * 4) / _note_value;
145 b += Evoral::Beats::ticks (offset.ticks);
151 TempoMetric::superclock_per_note_type_at_superclock (superclock_t sc) const
153 return superclocks_per_note_type () * expm1 (_c_per_superclock * sc);
157 TempoMetric::superclocks_per_grid (framecnt_t sr) const
159 return (superclock_ticks_per_second * Meter::note_value()) / (note_types_per_minute() / Tempo::note_type());
163 TempoMetric::superclocks_per_bar (framecnt_t sr) const
165 return superclocks_per_grid (sr) * _divisions_per_bar;
173 Tt----|-----------------*|
174 Ta----|--------------|* |
180 _______________|___|____
181 time a t (next tempo)
184 Duration in beats at time a is the integral of some Tempo function.
185 In our case, the Tempo function (Tempo at time t) is
188 >>1/S(t) = (1/S0)(e^ct) => (1/S)(t) = (e^(ct))/S0 => S(t) = S0/(e^(ct))
190 with function constant
193 >>c = log ((1/Sa)/(1/S0)) / a => c = log (S0/Sa) / a
198 >>a = log ((1/Ta)/(1/S0) / c => a = log (S0/Sa) / c
200 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
201 b(t) = T0(e^(ct) - 1) / c
203 >>b(t) = 1/S0(e^(ct) - 1) / c => b(t) = (e^(ct) - 1) / (c * S0)
205 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
206 t(b) = log((c.b / T0) + 1) / c
208 >>t(b) = log((c*b / (1/S0)) + 1) / c => t(b) = log ((c*b * S0) + 1) / c
210 The time t at which Tempo T occurs is a as above:
211 t(T) = log(T / T0) / c
213 >> t(1/S) = log ((1/S) / (1/S0) /c => t(1/S) = log (S0/S) / c
215 The beat at which a Tempo T occurs is:
218 >> b(1/S) = (1/S - 1/S0) / c
220 The Tempo at which beat b occurs is:
223 >> T(b) = b.c + (1/S0)
225 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
226 Our problem is that we usually don't know t.
227 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
228 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
229 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
231 By substituting our expanded t as a in the c function above, our problem is reduced to:
232 c = T0 (e^(log (Ta / T0)) - 1) / b
234 >> c = (1/S0) (e^(log ((1/Sa) / (1/S0))) - 1) / b => c = (1/S0) (e^(log (S0/Sa)) - 1) / b => c (e^(log (S0/Sa)) - 1) / (b * S0)
236 Of course the word 'beat' has been left loosely defined above.
237 In music, a beat is defined by the musical pulse (which comes from the tempo)
238 and the meter in use at a particular time (how many pulse divisions there are in one bar).
239 It would be more accurate to substitute the work 'pulse' for 'beat' above.
243 /* equation to compute c is:
245 * c = log (Ta / T0) / a
249 * a : time into section (from section start
250 * T0 : tempo at start of section
251 * Ta : tempo at time a into section
255 * log (Ta / T0) / (time-units) => C is in per-time-units (1/time-units)
257 * so the question is what are the units of a, and thus c?
259 * we could use ANY time units (because we can measure a in any time units)
260 * but whichever one we pick dictates how we can use c in the future since
261 * all subsequent computations will need to use the same time units.
265 * pulses ... whole notes, possibly useful, since we can use it for any other note_type
266 * quarter notes ... linearly related to pulses
267 * beats ... not a fixed unit of time
268 * minutes ... linearly related to superclocks
269 * samples ... needs sample rate
270 * superclocks ... frequently correct
272 * so one answer might be to compute c in two different units so that we have both available.
274 * hence, compute_c_superclocks() and compute_c_pulses()
278 TempoMetric::compute_c_superclock (framecnt_t sr, superclock_t end_scpqn, superclock_t superclock_duration)
280 if ((superclocks_per_quarter_note() == end_scpqn) || !ramped()) {
281 _c_per_superclock = 0.0;
285 _c_per_superclock = log ((double) superclocks_per_quarter_note () / end_scpqn) / superclock_duration;
288 TempoMetric::compute_c_quarters (framecnt_t sr, superclock_t end_scpqn, Evoral::Beats const & quarter_duration)
290 if ((superclocks_per_quarter_note () == end_scpqn) || !ramped()) {
291 _c_per_quarter = 0.0;
295 _c_per_quarter = log (superclocks_per_quarter_note () / (double) end_scpqn) / quarter_duration.to_double();
299 TempoMetric::superclock_at_qn (Evoral::Beats const & qn) const
301 if (_c_per_quarter == 0.0) {
302 /* not ramped, use linear */
303 return llrint (superclocks_per_quarter_note () * qn.to_double());
306 return llrint (superclocks_per_quarter_note() * (log1p (_c_per_quarter * qn.to_double()) / _c_per_quarter));
310 TempoMapPoint::quarters_at (superclock_t sc) const
312 /* This TempoMapPoint must already have a fully computed metric and position */
315 return _quarters + Evoral::Beats ((sc - _sclock) / (double) (metric().superclocks_per_quarter_note ()));
318 return _quarters + Evoral::Beats (expm1 (metric().c_per_superclock() * (sc - _sclock)) / (metric().c_per_superclock() * metric().superclocks_per_quarter_note ()));
322 TempoMapPoint::quarters_at (Timecode::BBT_Time const & bbt) const
324 /* This TempoMapPoint must already have a fully computed metric and position */
326 Timecode::BBT_Offset offset = metric().bbt_delta (bbt, _bbt);
327 return _quarters + metric().to_quarters (offset);
331 TempoMapPoint::bbt_at (Evoral::Beats const & qn) const
333 /* This TempoMapPoint must already have a fully computed metric and position */
335 Evoral::Beats quarters_delta = qn - _quarters;
336 int32_t ticks_delta = quarters_delta.to_ticks (Evoral::Beats::PPQN);
337 return metric().bbt_add (_bbt, Timecode::BBT_Offset (0, 0, ticks_delta));
340 TempoMap::TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, framecnt_t sr)
343 TempoMapPoint tmp (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), initial_tempo, initial_meter, 0, Evoral::Beats(), Timecode::BBT_Time(), AudioTime);
344 _points.push_back (tmp);
348 TempoMap::meter_at (superclock_t sc) const
350 Glib::Threads::RWLock::ReaderLock lm (_lock);
351 return meter_at_locked (sc);
355 TempoMap::meter_at (Evoral::Beats const & b) const
357 Glib::Threads::RWLock::ReaderLock lm (_lock);
358 return meter_at_locked (b);
362 TempoMap::meter_at (Timecode::BBT_Time const & bbt) const
364 Glib::Threads::RWLock::ReaderLock lm (_lock);
365 return meter_at_locked (bbt);
369 TempoMap::tempo_at (superclock_t sc) const
371 Glib::Threads::RWLock::ReaderLock lm (_lock);
372 return tempo_at_locked (sc);
376 TempoMap::tempo_at (Evoral::Beats const &b) const
378 Glib::Threads::RWLock::ReaderLock lm (_lock);
379 return tempo_at_locked (b);
383 TempoMap::tempo_at (Timecode::BBT_Time const & bbt) const
385 Glib::Threads::RWLock::ReaderLock lm (_lock);
386 return tempo_at_locked (bbt);
390 TempoMap::rebuild (superclock_t limit)
392 Glib::Threads::RWLock::WriterLock lm (_lock);
394 /* step one: remove all implicit points after a dirty explicit point */
397 TempoMapPoints::iterator tmp = _points.begin();
398 TempoMapPoints::iterator first_explicit_dirty = _points.end();
400 while ((tmp != _points.end()) && (tmp->is_implicit() || (tmp->is_explicit() && !tmp->dirty ()))) {
404 first_explicit_dirty = tmp;
406 if (first_explicit_dirty == _points.end()) {
407 /* nothing is dirty */
411 /* remove all implicit points, because we're going to recalculate them all */
413 while (tmp != _points.end()) {
414 TempoMapPoints::iterator next = tmp;
417 if (tmp->is_implicit()) {
424 /* compute C-by-quarters for all ramped sections, because we need it shortly */
426 for (tmp = first_explicit_dirty; tmp != _points.end(); ) {
427 TempoMapPoints::iterator nxt = tmp;
430 if (tmp->ramped() && (nxt != _points.end())) {
431 tmp->compute_c_quarters (_sample_rate, nxt->metric().superclocks_per_quarter_note (), nxt->quarters() - tmp->quarters());
437 TempoMapPoints::iterator prev = _points.end();
439 /* Compute correct quarter-note and superclock times for all music-time locked explicit points */
441 for (tmp = first_explicit_dirty; tmp != _points.end(); ) {
443 TempoMapPoints::iterator next = tmp;
446 if (prev != _points.end()) {
447 if ((tmp->lock_style() == MusicTime)) {
448 /* determine superclock and quarter note time for this (music-time) locked point */
450 Evoral::Beats qn = prev->quarters_at (tmp->bbt());
451 superclock_t sc = prev->sclock() + prev->metric().superclock_at_qn (qn - prev->quarters());
453 if (qn != tmp->quarters() || tmp->sclock() != sc) {
454 tmp->set_quarters (qn);
455 tmp->set_sclock (sc);
456 _points.sort (TempoMapPoint::SuperClockComparator());
466 /* _points is guaranteed sorted in superclock and quarter note order. It may not be sorted BBT order because of re-ordering
467 * of music-time locked points.
470 prev = _points.end();
472 /* step two: add new implicit points between each pair of explicit
473 * points, after the dirty explicit point
476 bool hit_dirty = false;
478 for (tmp = _points.begin(); tmp != _points.end(); ) {
488 TempoMapPoints::iterator next = tmp;
492 if (prev != _points.end()) {
493 if ((tmp->lock_style() == AudioTime)) {
494 /* audio-locked explicit point: recompute it's BBT and quarter-note position since this may have changed */
495 tmp->set_quarters (prev->quarters_at (tmp->sclock()));
496 if (static_cast<Meter>(tmp->metric()) != static_cast<Meter>(prev->metric())) {
497 /* new meter, must be on bar/measure start */
498 tmp->set_bbt (prev->metric().round_up_to_bar (prev->bbt_at (tmp->quarters())));
500 /* no meter change, tempo change required to be on beat */
501 tmp->set_bbt (prev->bbt_at (tmp->quarters()).round_up_to_beat());
506 superclock_t sc = tmp->sclock();
507 Evoral::Beats qn (tmp->quarters ());
508 Timecode::BBT_Time bbt (tmp->bbt());
509 const bool ramped = tmp->ramped () && next != _points.end();
511 /* Evoral::Beats are really quarter notes. This counts how many quarter notes
512 there are between grid points in this section of the tempo map.
514 const Evoral::Beats qn_step = (Evoral::Beats (1) * 4) / tmp->metric().note_value();
516 /* compute implicit points as far as the next explicit point, or limit,
517 whichever comes first.
520 const superclock_t sc_limit = (next == _points.end() ? limit : (*next).sclock());
524 /* define next beat in superclocks, beats and bbt */
527 bbt = tmp->metric().bbt_add (bbt, Timecode::BBT_Offset (0, 1, 0));
530 sc += tmp->metric().superclocks_per_note_type();
532 sc = tmp->sclock() + tmp->metric().superclock_at_qn (qn - tmp->quarters());
535 if (sc >= sc_limit) {
539 _points.insert (next, TempoMapPoint (*tmp, sc, qn, bbt));
542 (*tmp).set_dirty (false);
549 TempoMap::set_tempo_and_meter (Tempo const & tempo, Meter const & meter, superclock_t sc, bool ramp, bool flexible)
551 /* CALLER MUST HOLD LOCK */
553 assert (!_points.empty());
555 /* special case: first map entry is later than the new point */
557 if (_points.front().sclock() > sc) {
558 /* first point is later than sc. There's no iterator to reference a point at or before sc */
560 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
561 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
565 Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat();
566 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
568 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, b, bbt, AudioTime, ramp));
572 /* special case #3: only one map entry, at the same time as the new point.
573 This is the common case when editing tempo/meter in a session with a single tempo/meter
576 if (_points.size() == 1 && _points.front().sclock() == sc) {
578 *((Tempo*) &_points.front().metric()) = tempo;
579 *((Meter*) &_points.front().metric()) = meter;
580 _points.front().make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter));
584 /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */
586 TempoMapPoints::iterator i = iterator_at (sc);
587 TempoMapPoints::iterator nxt = i;
590 if (i->sclock() == sc) {
592 *((Tempo*) &i->metric()) = tempo;
593 *((Meter*) &i->metric()) = meter;
594 i->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter));
599 if (!flexible && (sc - i->sclock() < i->metric().superclocks_per_note_type())) {
600 cerr << "new tempo too close to previous ...\n";
604 TempoMapPoints::iterator e (i);
605 while (!e->is_explicit() && e != _points.begin()) {
609 if (e->metric().ramped()) {
610 /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it
611 and thus defines the new ramp distance.
613 e->compute_c_superclock (_sample_rate, tempo.superclocks_per_quarter_note (), sc);
616 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
617 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
621 Evoral::Beats qn = i->quarters_at (sc).round_to_beat();
623 /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc"
626 Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat ();
628 /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before"
631 if (i != _points.end()) {
635 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, qn, bbt, AudioTime, ramp));
640 TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp)
642 Glib::Threads::RWLock::WriterLock lm (_lock);
644 assert (!_points.empty());
646 /* special case: first map entry is later than the new point */
648 if (_points.front().sclock() > sc) {
649 /* first point is later than sc. There's no iterator to reference a point at or before sc */
651 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
652 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
656 Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat();
657 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
659 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, t, _points.front().metric(), sc, b, bbt, AudioTime, ramp));
663 /* special case #3: only one map entry, at the same time as the new point.
664 This is the common case when editing tempo/meter in a session with a single tempo/meter
667 if (_points.size() == 1 && _points.front().sclock() == sc) {
669 *((Tempo*) &_points.front().metric()) = t;
670 _points.front().make_explicit (TempoMapPoint::ExplicitTempo);
674 /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */
676 TempoMapPoints::iterator i = iterator_at (sc);
677 TempoMapPoints::iterator nxt = i;
680 if (i->sclock() == sc) {
682 *((Tempo*) &i->metric()) = t;
683 i->make_explicit (TempoMapPoint::ExplicitTempo);
688 if (sc - i->sclock() < i->metric().superclocks_per_note_type()) {
689 cerr << "new tempo too close to previous ...\n";
693 Meter const & meter (i->metric());
695 TempoMapPoints::iterator e (i);
696 while (!e->is_explicit() && e != _points.begin()) {
700 if (e->metric().ramped()) {
701 /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it
702 and thus defines the new ramp distance.
704 e->compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc);
707 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
708 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
712 Evoral::Beats qn = i->quarters_at (sc).round_to_beat();
714 /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc"
717 Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat ();
719 /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before"
722 if (i != _points.end()) {
725 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, sc, qn, bbt, AudioTime, ramp));
730 TempoMap::set_tempo (Tempo const & t, Timecode::BBT_Time const & bbt, bool ramp)
732 Glib::Threads::RWLock::WriterLock lm (_lock);
734 /* tempo changes are required to be on-beat */
736 Timecode::BBT_Time on_beat = bbt.round_up_to_beat();
738 assert (!_points.empty());
740 if (_points.front().bbt() > on_beat) {
741 cerr << "Cannot insert tempo at " << bbt << " before first point at " << _points.front().bbt() << endl;
745 if (_points.size() == 1 && _points.front().bbt() == on_beat) {
747 *((Tempo*) &_points.front().metric()) = t;
748 _points.front().make_explicit (TempoMapPoint::ExplicitTempo);
752 TempoMapPoints::iterator i = iterator_at (on_beat);
754 if (i->bbt() == on_beat) {
755 *((Tempo*) &i->metric()) = t;
756 i->make_explicit (TempoMapPoint::ExplicitTempo);
760 Meter const & meter (i->metric());
763 /* stick a prototype music-locked point up front and let ::rebuild figure out the superclock and quarter time */
764 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, 0, Evoral::Beats(), on_beat, MusicTime, ramp));
769 TempoMap::set_meter (Meter const & m, Timecode::BBT_Time const & bbt)
771 Glib::Threads::RWLock::WriterLock lm (_lock);
772 Timecode::BBT_Time measure_start (m.round_up_to_bar (bbt));
774 assert (!_points.empty());
776 if (_points.front().bbt() > measure_start) {
777 cerr << "Cannot insert meter at " << bbt << " before first point at " << _points.front().bbt() << endl;
781 if (_points.size() == 1 && _points.front().bbt() == measure_start) {
783 *((Meter*) &_points.front().metric()) = m;
784 _points.front().make_explicit (TempoMapPoint::ExplicitMeter);
788 TempoMapPoints::iterator i = iterator_at (measure_start);
790 if (i->bbt() == measure_start) {
791 *((Meter*) &i->metric()) = m;
792 i->make_explicit (TempoMapPoint::ExplicitMeter);
796 Evoral::Beats qn = i->quarters_at (measure_start);
797 superclock_t sc = i->sclock() + i->metric().superclock_at_qn (qn);
799 Tempo const & tempo (i->metric());
802 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, qn, measure_start, MusicTime));
807 TempoMap::set_meter (Meter const & m, superclock_t sc)
809 Glib::Threads::RWLock::WriterLock lm (_lock);
810 assert (!_points.empty());
812 /* special case #2: first map entry is later than the new point */
814 if (_points.front().sclock() > sc) {
815 /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
816 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
820 Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat();
821 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
823 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitMeter, _points.front().metric(), m, sc, b, bbt, AudioTime));
827 /* special case #3: only one map entry, at the same time as the new point.
829 This is the common case when editing tempo/meter in a session with a single tempo/meter
832 if (_points.size() == 1 && _points.front().sclock() == sc) {
834 *((Meter*) &_points.front().metric()) = m;
835 _points.front().make_explicit (TempoMapPoint::ExplicitMeter);
839 TempoMapPoints::iterator i = iterator_at (sc);
841 if (i->sclock() == sc) {
843 *((Meter*) &i->metric()) = m;
845 /* enforce rule described below regarding meter change positions */
847 if (i->bbt().beats != 1) {
848 i->set_bbt (Timecode::BBT_Time (i->bbt().bars + 1, 1, 0));
851 i->make_explicit (TempoMapPoint::ExplicitMeter);
856 if (sc - i->sclock() < i->metric().superclocks_per_note_type()) {
857 cerr << "new tempo too close to previous ...\n";
861 /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
862 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
866 Evoral::Beats b = i->quarters_at (sc).round_to_beat();
868 /* rule: all Meter changes must start a new measure. So determine the nearest, lower beat to "sc". If this is not
869 the first division of the measure, move to the next measure.
872 Timecode::BBT_Time bbt = i->bbt_at (b).round_down_to_beat ();
874 if (bbt.beats != 1) {
880 Tempo const & tempo (i->metric());
883 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, b, bbt, AudioTime));
887 TempoMapPoints::iterator
888 TempoMap::iterator_at (superclock_t sc)
890 /* CALLER MUST HOLD LOCK */
892 if (_points.empty()) {
893 throw EmptyTempoMapException();
896 if (_points.size() == 1) {
897 return _points.begin();
900 /* Construct an arbitrary TempoMapPoint. The only property we care about is it's superclock time,
901 so other values used in the constructor are arbitrary and irrelevant.
904 TempoMetric const & metric (_points.front().metric());
905 const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, sc, Evoral::Beats(), Timecode::BBT_Time(), AudioTime);
906 TempoMapPoint::SuperClockComparator scmp;
908 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, scmp);
910 if (tmp != _points.begin()) {
917 TempoMapPoints::iterator
918 TempoMap::iterator_at (Evoral::Beats const & qn)
920 /* CALLER MUST HOLD LOCK */
922 if (_points.empty()) {
923 throw EmptyTempoMapException();
926 if (_points.size() == 1) {
927 return _points.begin();
930 /* Construct an arbitrary TempoMapPoint. The only property we care about is its quarters time,
931 so other values used in the constructor are arbitrary and irrelevant.
934 TempoMetric const & metric (_points.front().metric());
935 const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, 0, qn, Timecode::BBT_Time(), AudioTime);
936 TempoMapPoint::QuarterComparator bcmp;
938 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp);
940 if (tmp != _points.begin()) {
947 TempoMapPoints::iterator
948 TempoMap::iterator_at (Timecode::BBT_Time const & bbt)
950 /* CALLER MUST HOLD LOCK */
952 if (_points.empty()) {
953 throw EmptyTempoMapException();
956 if (_points.size() == 1) {
957 return _points.begin();
960 /* Construct an arbitrary TempoMapPoint. The only property we care about is its bbt time,
961 so other values used in the constructor are arbitrary and irrelevant.
964 TempoMetric const & metric (_points.front().metric());
965 const TempoMapPoint tp (TempoMapPoint::Flag(0), metric, metric, 0, Evoral::Beats(), bbt, MusicTime);
966 TempoMapPoint::BBTComparator bcmp;
968 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp);
970 if (tmp != _points.begin()) {
978 TempoMap::bbt_at (superclock_t sc) const
980 Glib::Threads::RWLock::ReaderLock lm (_lock);
981 return bbt_at_locked (sc);
985 TempoMap::bbt_at_locked (superclock_t sc) const
987 TempoMapPoint point (const_point_at (sc));
988 Evoral::Beats b ((sc - point.sclock()) / (double) point.metric().superclocks_per_quarter_note());
989 return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, b.get_beats(), b.get_ticks()));
993 TempoMap::bbt_at (Evoral::Beats const & qn) const
995 Glib::Threads::RWLock::ReaderLock lm (_lock);
996 return bbt_at_locked (qn);
1000 TempoMap::bbt_at_locked (Evoral::Beats const & qn) const
1002 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1003 TempoMapPoint const & point (const_point_at (qn));
1004 Evoral::Beats delta (qn - point.quarters());
1005 return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, delta.get_beats(), delta.get_ticks()));
1009 TempoMap::superclock_at (Evoral::Beats const & qn) const
1011 Glib::Threads::RWLock::ReaderLock lm (_lock);
1012 return superclock_at_locked (qn);
1016 TempoMap::superclock_at_locked (Evoral::Beats const & qn) const
1018 assert (!_points.empty());
1020 /* only the quarters property matters, since we're just using it to compare */
1021 const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, qn, Timecode::BBT_Time(), MusicTime);
1022 TempoMapPoint::QuarterComparator bcmp;
1024 TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp);
1026 if (i == _points.end()) {
1030 /* compute distance from reference point to b. Remember that Evoral::Beats is always interpreted as quarter notes */
1032 const Evoral::Beats q_delta = qn - i->quarters();
1033 superclock_t sclock_delta = i->metric().superclock_at_qn (q_delta);
1034 return i->sclock() + sclock_delta;
1038 TempoMap::superclock_at (Timecode::BBT_Time const & bbt) const
1040 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1041 return superclock_at_locked (bbt);
1045 TempoMap::superclock_at_locked (Timecode::BBT_Time const & bbt) const
1047 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1048 assert (!_points.empty());
1050 /* only the bbt property matters, since we're just using it to compare */
1051 const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, Evoral::Beats(), bbt, MusicTime);
1052 TempoMapPoint::BBTComparator bcmp;
1054 TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp);
1056 if (i == _points.end()) {
1060 /* this computes the distance from the point, in beats whose size is
1061 determined by the meter.
1064 const Timecode::BBT_Offset delta = i->metric().bbt_delta (bbt, i->bbt());
1066 /* convert to quarter notes */
1067 const int32_t ticks = delta.ticks + (Evoral::Beats::PPQN * delta.beats * 4) / i->metric().note_value();
1069 /* get distance in superclocks */
1070 return i->sclock() + i->metric().superclock_at_qn (Evoral::Beats::ticks (ticks));
1074 TempoMap::set_sample_rate (framecnt_t new_sr)
1076 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1077 double ratio = new_sr / (double) _sample_rate;
1079 for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) {
1080 i->map_reset_set_sclock_for_sr_change (llrint (ratio * i->sclock()));
1085 TempoMap::dump (std::ostream& ostr)
1087 Glib::Threads::RWLock::ReaderLock lm (_lock);
1092 TempoMap::dump_locked (std::ostream& ostr)
1094 ostr << "\n\n------------\n";
1095 for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) {
1096 ostr << *i << std::endl;
1101 TempoMap::remove_explicit_point (superclock_t sc)
1103 Glib::Threads::RWLock::WriterLock lm (_lock);
1104 TempoMapPoints::iterator p = iterator_at (sc);
1106 if (p->sclock() == sc) {
1112 TempoMap::move_to (superclock_t current, superclock_t destination, bool push)
1114 Glib::Threads::RWLock::WriterLock lm (_lock);
1115 TempoMapPoints::iterator p = iterator_at (current);
1117 if (p->sclock() != current) {
1118 cerr << "No point @ " << current << endl;
1122 /* put a "dirty" flag in at the nearest explicit point to the removal point
1125 if (p != _points.begin()) {
1126 TempoMapPoints::iterator prev (p);
1128 while (prev != _points.begin() && !prev->is_explicit()) {
1131 prev->set_dirty (true);
1133 TempoMapPoints::iterator nxt (p);
1135 while (nxt != _points.end() && !nxt->is_explicit()) {
1138 if (nxt != _points.end()) {
1139 nxt->set_dirty (true);
1143 if (!set_tempo_and_meter (p->metric(), p->metric(), destination, p->ramped(), true)) {
1149 p = iterator_at (destination);
1152 const superclock_t delta = destination - current;
1154 while (p != _points.end()) {
1155 p->set_sclock (p->sclock() + delta);
1166 TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, Evoral::Beats const & resolution)
1168 Glib::Threads::RWLock::ReaderLock lm (_lock);
1169 TempoMapPoints::iterator p = iterator_at (start);
1171 while (p != _points.end() && p->sclock() < start) {
1175 if (resolution == Evoral::Beats()) {
1176 /* just hand over the points as-is */
1177 while (p != _points.end() && p->sclock() < end) {
1184 superclock_t pos = p->sclock();
1186 TempoMapPoints::iterator nxt = p;
1189 while ((p != _points.end()) && (pos < end)) {
1190 /* recompute grid down to @param resolution level
1193 superclock_t sclock_delta = p->metric().superclock_at_qn (qpos);
1195 ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo),
1196 p->metric(), p->metric(),
1197 p->sclock() + sclock_delta,
1198 p->quarters() + qpos,
1199 p->metric().bbt_add (p->bbt(), Timecode::BBT_Offset (0, qpos.get_beats(), qpos.get_ticks())),
1204 pos += sclock_delta;
1206 if (pos >= nxt->sclock()) {
1215 TempoMap::get_bar_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, int32_t bar_gap)
1217 Glib::Threads::RWLock::ReaderLock lm (_lock);
1219 for (TempoMapPoints::iterator p = iterator_at (start); (p != _points.end()) && (p->sclock() < end); ++p) {
1221 if ((p->sclock() >= start) && (p->bbt().beats == 1) && ((p->bbt().bars == 1) || (p->bbt().bars % bar_gap == 0))) {
1222 ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo),
1223 p->metric(), p->metric(),
1234 operator<<(std::ostream& str, Meter const & m)
1236 return str << m.divisions_per_bar() << '/' << m.note_value();
1240 operator<<(std::ostream& str, Tempo const & t)
1242 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() << ')';
1246 operator<<(std::ostream& str, TempoMapPoint const & tmp)
1248 str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second
1249 << (tmp.is_explicit() ? " EXP" : " imp")
1250 << " qn " << tmp.quarters ()
1251 << " bbt " << tmp.bbt()
1252 << " lock to " << tmp.lock_style()
1255 if (tmp.is_explicit()) {
1256 str << " tempo " << *((Tempo*) &tmp.metric())
1257 << " meter " << *((Meter*) &tmp.metric())
1261 if (tmp.is_explicit() && tmp.ramped()) {
1262 str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter();
1269 #define SAMPLERATE 48000
1270 #define SECONDS_TO_SUPERCLOCK(s) (superclock_ticks_per_second * s)
1279 using namespace Timecode;
1282 BBT_Time b1 (1,1,1919);
1283 BBT_Time n1 (-1,1,1919);
1284 std::vector<Meter> meters;
1286 meters.push_back (Meter (4, 4));
1287 meters.push_back (Meter (5, 8));
1288 meters.push_back (Meter (11, 7));
1289 meters.push_back (Meter (3, 4));
1291 #define PRINT_RESULT(m,str,op,op1,Bars,Beats,Ticks) cout << m << ' ' << (op1) << ' ' << str << ' ' << BBT_Offset ((Bars),(Beats),(Ticks)) << " = " << m.op ((op1), BBT_Offset ((Bars), (Beats), (Ticks))) << endl;
1293 for (std::vector<Meter>::iterator m = meters.begin(); m != meters.end(); ++m) {
1294 for (int B = 1; B < 4; ++B) {
1295 for (int b = 1; b < 13; ++b) {
1296 PRINT_RESULT((*m), "+", bbt_add, a, B, b, 0);
1297 PRINT_RESULT((*m), "+", bbt_add, a, B, b, 1);
1298 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN/2);
1299 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN);
1300 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN - 1);
1301 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN - 2);
1303 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 0);
1304 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 1);
1305 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN/2);
1306 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN);
1307 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN - 1);
1308 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN - 2);
1310 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 0);
1311 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 1);
1312 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN/2);
1313 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN);
1314 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN - 1);
1315 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN - 2);
1319 for (int B = 1; B < 4; ++B) {
1320 for (int b = 1; b < 13; ++b) {
1321 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 0);
1322 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 1);
1323 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN/2);
1324 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN);
1325 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN - 1);
1326 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN - 2);
1335 TempoMap tmap (Tempo (140), Meter (4,4), SAMPLERATE);
1340 tmap.set_tempo (Tempo (7), SECONDS_TO_SUPERCLOCK(7));
1341 tmap.set_tempo (Tempo (23), SECONDS_TO_SUPERCLOCK(23));
1342 tmap.set_tempo (Tempo (24), SECONDS_TO_SUPERCLOCK(24), true);
1343 tmap.set_tempo (Tempo (40), SECONDS_TO_SUPERCLOCK(28), true);
1344 tmap.set_tempo (Tempo (100), SECONDS_TO_SUPERCLOCK(100));
1345 tmap.set_tempo (Tempo (123), SECONDS_TO_SUPERCLOCK(23));
1347 tmap.set_meter (Meter (3, 4), SECONDS_TO_SUPERCLOCK(23));
1348 tmap.set_meter (Meter (5, 8), SECONDS_TO_SUPERCLOCK(100));
1349 tmap.set_meter (Meter (5, 7), SECONDS_TO_SUPERCLOCK(7));
1350 tmap.set_meter (Meter (4, 4), SECONDS_TO_SUPERCLOCK(24));
1351 tmap.set_meter (Meter (11, 7), SECONDS_TO_SUPERCLOCK(23));
1353 tmap.set_meter (Meter (3, 8), Timecode::BBT_Time (17, 1, 0));
1355 tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
1356 tmap.dump (std::cout);
1358 if (tmap.move_to (SECONDS_TO_SUPERCLOCK(23), SECONDS_TO_SUPERCLOCK (72))) {
1359 tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
1360 tmap.dump (std::cout);
1363 TempoMapPoints grid;
1364 tmap.get_grid (grid, SECONDS_TO_SUPERCLOCK (12.3), SECONDS_TO_SUPERCLOCK (44), Evoral::Beats::ticks ((Evoral::Beats::PPQN * 4) / 12));
1365 cout << "grid contains " << grid.size() << endl;
1366 for (TempoMapPoints::iterator p = grid.begin(); p != grid.end(); ++p) {
1370 TempoMapPoints bbt_grid;
1371 tmap.get_bar_grid (bbt_grid, SECONDS_TO_SUPERCLOCK (0), SECONDS_TO_SUPERCLOCK (100), 4);
1372 cout << "bbt_grid contains " << bbt_grid.size() << endl;
1373 for (TempoMapPoints::iterator p = bbt_grid.begin(); p != bbt_grid.end(); ++p) {