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::set_map (TempoMap* m)
316 TempoMapPoint::set_dirty (bool yn)
321 _map->set_dirty (true);
327 TempoMapPoint::quarters_at (superclock_t sc) const
329 /* This TempoMapPoint must already have a fully computed metric and position */
332 return _quarters + Evoral::Beats ((sc - _sclock) / (double) (metric().superclocks_per_quarter_note ()));
335 return _quarters + Evoral::Beats (expm1 (metric().c_per_superclock() * (sc - _sclock)) / (metric().c_per_superclock() * metric().superclocks_per_quarter_note ()));
339 TempoMapPoint::quarters_at (Timecode::BBT_Time const & bbt) const
341 /* This TempoMapPoint must already have a fully computed metric and position */
343 Timecode::BBT_Offset offset = metric().bbt_delta (bbt, _bbt);
344 return _quarters + metric().to_quarters (offset);
348 TempoMapPoint::bbt_at (Evoral::Beats const & qn) const
350 /* This TempoMapPoint must already have a fully computed metric and position */
352 Evoral::Beats quarters_delta = qn - _quarters;
353 int32_t ticks_delta = quarters_delta.to_ticks (Evoral::Beats::PPQN);
354 return metric().bbt_add (_bbt, Timecode::BBT_Offset (0, 0, ticks_delta));
357 TempoMap::TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, framecnt_t sr)
361 TempoMapPoint tmp (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), initial_tempo, initial_meter, 0, Evoral::Beats(), Timecode::BBT_Time(), AudioTime);
362 _points.push_back (tmp);
366 TempoMap::set_dirty (bool yn)
372 TempoMap::meter_at (superclock_t sc) const
374 Glib::Threads::RWLock::ReaderLock lm (_lock);
375 return meter_at_locked (sc);
379 TempoMap::meter_at (Evoral::Beats const & b) const
381 Glib::Threads::RWLock::ReaderLock lm (_lock);
382 return meter_at_locked (b);
386 TempoMap::meter_at (Timecode::BBT_Time const & bbt) const
388 Glib::Threads::RWLock::ReaderLock lm (_lock);
389 return meter_at_locked (bbt);
393 TempoMap::tempo_at (superclock_t sc) const
395 Glib::Threads::RWLock::ReaderLock lm (_lock);
396 return tempo_at_locked (sc);
400 TempoMap::tempo_at (Evoral::Beats const &b) const
402 Glib::Threads::RWLock::ReaderLock lm (_lock);
403 return tempo_at_locked (b);
407 TempoMap::tempo_at (Timecode::BBT_Time const & bbt) const
409 Glib::Threads::RWLock::ReaderLock lm (_lock);
410 return tempo_at_locked (bbt);
414 TempoMap::rebuild (superclock_t limit)
416 Glib::Threads::RWLock::WriterLock lm (_lock);
418 /* step one: remove all implicit points after a dirty explicit point */
421 TempoMapPoints::iterator tmp = _points.begin();
422 TempoMapPoints::iterator first_explicit_dirty = _points.end();
424 while ((tmp != _points.end()) && (tmp->is_implicit() || (tmp->is_explicit() && !tmp->dirty ()))) {
428 first_explicit_dirty = tmp;
430 if (first_explicit_dirty == _points.end()) {
431 /* nothing is dirty */
435 /* remove all implicit points, because we're going to recalculate them all */
437 while (tmp != _points.end()) {
438 TempoMapPoints::iterator next = tmp;
441 if (tmp->is_implicit()) {
448 /* compute C-by-quarters for all ramped sections, because we need it shortly */
450 for (tmp = first_explicit_dirty; tmp != _points.end(); ) {
451 TempoMapPoints::iterator nxt = tmp;
454 if (tmp->ramped() && (nxt != _points.end())) {
455 tmp->compute_c_quarters (_sample_rate, nxt->metric().superclocks_per_quarter_note (), nxt->quarters() - tmp->quarters());
461 TempoMapPoints::iterator prev = _points.end();
463 /* Compute correct quarter-note and superclock times for all music-time locked explicit points */
465 for (tmp = first_explicit_dirty; tmp != _points.end(); ) {
467 TempoMapPoints::iterator next = tmp;
470 if (prev != _points.end()) {
471 if ((tmp->lock_style() == MusicTime)) {
472 /* determine superclock and quarter note time for this (music-time) locked point */
474 Evoral::Beats qn = prev->quarters_at (tmp->bbt());
475 superclock_t sc = prev->sclock() + prev->metric().superclock_at_qn (qn - prev->quarters());
477 if (qn != tmp->quarters() || tmp->sclock() != sc) {
478 tmp->set_quarters (qn);
479 tmp->set_sclock (sc);
480 _points.sort (TempoMapPoint::SuperClockComparator());
490 /* _points is guaranteed sorted in superclock and quarter note order. It may not be sorted BBT order because of re-ordering
491 * of music-time locked points.
494 prev = _points.end();
496 /* step two: add new implicit points between each pair of explicit
497 * points, after the dirty explicit point
500 bool hit_dirty = false;
502 for (tmp = _points.begin(); tmp != _points.end(); ) {
512 TempoMapPoints::iterator next = tmp;
516 if (prev != _points.end()) {
517 if ((tmp->lock_style() == AudioTime)) {
518 /* audio-locked explicit point: recompute it's BBT and quarter-note position since this may have changed */
519 tmp->set_quarters (prev->quarters_at (tmp->sclock()));
520 if (static_cast<Meter>(tmp->metric()) != static_cast<Meter>(prev->metric())) {
521 /* new meter, must be on bar/measure start */
522 tmp->set_bbt (prev->metric().round_up_to_bar (prev->bbt_at (tmp->quarters())));
524 /* no meter change, tempo change required to be on beat */
525 tmp->set_bbt (prev->bbt_at (tmp->quarters()).round_up_to_beat());
530 superclock_t sc = tmp->sclock();
531 Evoral::Beats qn (tmp->quarters ());
532 Timecode::BBT_Time bbt (tmp->bbt());
533 const bool ramped = tmp->ramped () && next != _points.end();
535 /* Evoral::Beats are really quarter notes. This counts how many quarter notes
536 there are between grid points in this section of the tempo map.
538 const Evoral::Beats qn_step = (Evoral::Beats (1) * 4) / tmp->metric().note_value();
540 /* compute implicit points as far as the next explicit point, or limit,
541 whichever comes first.
544 const superclock_t sc_limit = (next == _points.end() ? limit : (*next).sclock());
548 /* define next beat in superclocks, beats and bbt */
551 bbt = tmp->metric().bbt_add (bbt, Timecode::BBT_Offset (0, 1, 0));
554 sc += tmp->metric().superclocks_per_note_type();
556 sc = tmp->sclock() + tmp->metric().superclock_at_qn (qn - tmp->quarters());
559 if (sc >= sc_limit) {
563 _points.insert (next, TempoMapPoint (*tmp, sc, qn, bbt));
566 (*tmp).set_dirty (false);
573 TempoMap::set_tempo_and_meter (Tempo const & tempo, Meter const & meter, superclock_t sc, bool ramp, bool flexible)
575 /* CALLER MUST HOLD LOCK */
577 assert (!_points.empty());
579 /* special case: first map entry is later than the new point */
581 if (_points.front().sclock() > sc) {
582 /* first point is later than sc. There's no iterator to reference a point at or before sc */
584 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
585 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
589 Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat();
590 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
592 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, b, bbt, AudioTime, ramp));
596 /* special case #3: only one map entry, at the same time as the new point.
597 This is the common case when editing tempo/meter in a session with a single tempo/meter
600 if (_points.size() == 1 && _points.front().sclock() == sc) {
602 *((Tempo*) &_points.front().metric()) = tempo;
603 *((Meter*) &_points.front().metric()) = meter;
604 _points.front().make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter));
608 /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */
610 TempoMapPoints::iterator i = iterator_at (sc);
611 TempoMapPoints::iterator nxt = i;
614 if (i->sclock() == sc) {
616 *((Tempo*) &i->metric()) = tempo;
617 *((Meter*) &i->metric()) = meter;
618 i->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter));
623 if (!flexible && (sc - i->sclock() < i->metric().superclocks_per_note_type())) {
624 cerr << "new tempo too close to previous ...\n";
628 TempoMapPoints::iterator e (i);
629 while (!e->is_explicit() && e != _points.begin()) {
633 if (e->metric().ramped()) {
634 /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it
635 and thus defines the new ramp distance.
637 e->compute_c_superclock (_sample_rate, tempo.superclocks_per_quarter_note (), sc);
640 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
641 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
645 Evoral::Beats qn = i->quarters_at (sc).round_to_beat();
647 /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc"
650 Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat ();
652 /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before"
655 if (i != _points.end()) {
659 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, qn, bbt, AudioTime, ramp));
664 TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp)
666 Glib::Threads::RWLock::WriterLock lm (_lock);
668 assert (!_points.empty());
670 /* special case: first map entry is later than the new point */
672 if (_points.front().sclock() > sc) {
673 /* first point is later than sc. There's no iterator to reference a point at or before sc */
675 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
676 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
680 Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat();
681 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
683 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, t, _points.front().metric(), sc, b, bbt, AudioTime, ramp));
687 /* special case #3: only one map entry, at the same time as the new point.
688 This is the common case when editing tempo/meter in a session with a single tempo/meter
691 if (_points.size() == 1 && _points.front().sclock() == sc) {
693 *((Tempo*) &_points.front().metric()) = t;
694 _points.front().make_explicit (TempoMapPoint::ExplicitTempo);
698 /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */
700 TempoMapPoints::iterator i = iterator_at (sc);
701 TempoMapPoints::iterator nxt = i;
704 if (i->sclock() == sc) {
706 *((Tempo*) &i->metric()) = t;
707 i->make_explicit (TempoMapPoint::ExplicitTempo);
712 if (sc - i->sclock() < i->metric().superclocks_per_note_type()) {
713 cerr << "new tempo too close to previous ...\n";
717 Meter const & meter (i->metric());
719 TempoMapPoints::iterator e (i);
720 while (!e->is_explicit() && e != _points.begin()) {
724 if (e->metric().ramped()) {
725 /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it
726 and thus defines the new ramp distance.
728 e->compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc);
731 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
732 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
736 Evoral::Beats qn = i->quarters_at (sc).round_to_beat();
738 /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc"
741 Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat ();
743 /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before"
746 if (i != _points.end()) {
749 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, sc, qn, bbt, AudioTime, ramp));
754 TempoMap::set_tempo (Tempo const & t, Timecode::BBT_Time const & bbt, bool ramp)
756 Glib::Threads::RWLock::WriterLock lm (_lock);
758 /* tempo changes are required to be on-beat */
760 Timecode::BBT_Time on_beat = bbt.round_up_to_beat();
762 assert (!_points.empty());
764 if (_points.front().bbt() > on_beat) {
765 cerr << "Cannot insert tempo at " << bbt << " before first point at " << _points.front().bbt() << endl;
769 if (_points.size() == 1 && _points.front().bbt() == on_beat) {
771 *((Tempo*) &_points.front().metric()) = t;
772 _points.front().make_explicit (TempoMapPoint::ExplicitTempo);
776 TempoMapPoints::iterator i = iterator_at (on_beat);
778 if (i->bbt() == on_beat) {
779 *((Tempo*) &i->metric()) = t;
780 i->make_explicit (TempoMapPoint::ExplicitTempo);
784 Meter const & meter (i->metric());
787 /* stick a prototype music-locked point up front and let ::rebuild figure out the superclock and quarter time */
788 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, 0, Evoral::Beats(), on_beat, MusicTime, ramp));
793 TempoMap::set_meter (Meter const & m, Timecode::BBT_Time const & bbt)
795 Glib::Threads::RWLock::WriterLock lm (_lock);
796 Timecode::BBT_Time measure_start (m.round_up_to_bar (bbt));
798 assert (!_points.empty());
800 if (_points.front().bbt() > measure_start) {
801 cerr << "Cannot insert meter at " << bbt << " before first point at " << _points.front().bbt() << endl;
805 if (_points.size() == 1 && _points.front().bbt() == measure_start) {
807 *((Meter*) &_points.front().metric()) = m;
808 _points.front().make_explicit (TempoMapPoint::ExplicitMeter);
812 TempoMapPoints::iterator i = iterator_at (measure_start);
814 if (i->bbt() == measure_start) {
815 *((Meter*) &i->metric()) = m;
816 i->make_explicit (TempoMapPoint::ExplicitMeter);
820 Evoral::Beats qn = i->quarters_at (measure_start);
821 superclock_t sc = i->sclock() + i->metric().superclock_at_qn (qn);
823 Tempo const & tempo (i->metric());
826 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, qn, measure_start, MusicTime));
831 TempoMap::set_meter (Meter const & m, superclock_t sc)
833 Glib::Threads::RWLock::WriterLock lm (_lock);
834 assert (!_points.empty());
836 /* special case #2: first map entry is later than the new point */
838 if (_points.front().sclock() > sc) {
839 /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
840 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
844 Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat();
845 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
847 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitMeter, _points.front().metric(), m, sc, b, bbt, AudioTime));
851 /* special case #3: only one map entry, at the same time as the new point.
853 This is the common case when editing tempo/meter in a session with a single tempo/meter
856 if (_points.size() == 1 && _points.front().sclock() == sc) {
858 *((Meter*) &_points.front().metric()) = m;
859 _points.front().make_explicit (TempoMapPoint::ExplicitMeter);
863 TempoMapPoints::iterator i = iterator_at (sc);
865 if (i->sclock() == sc) {
867 *((Meter*) &i->metric()) = m;
869 /* enforce rule described below regarding meter change positions */
871 if (i->bbt().beats != 1) {
872 i->set_bbt (Timecode::BBT_Time (i->bbt().bars + 1, 1, 0));
875 i->make_explicit (TempoMapPoint::ExplicitMeter);
880 if (sc - i->sclock() < i->metric().superclocks_per_note_type()) {
881 cerr << "new tempo too close to previous ...\n";
885 /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
886 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
890 Evoral::Beats b = i->quarters_at (sc).round_to_beat();
892 /* rule: all Meter changes must start a new measure. So determine the nearest, lower beat to "sc". If this is not
893 the first division of the measure, move to the next measure.
896 Timecode::BBT_Time bbt = i->bbt_at (b).round_down_to_beat ();
898 if (bbt.beats != 1) {
904 Tempo const & tempo (i->metric());
907 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, b, bbt, AudioTime));
911 TempoMapPoints::iterator
912 TempoMap::iterator_at (superclock_t sc)
914 /* CALLER MUST HOLD LOCK */
916 if (_points.empty()) {
917 throw EmptyTempoMapException();
920 if (_points.size() == 1) {
921 return _points.begin();
924 /* Construct an arbitrary TempoMapPoint. The only property we care about is it's superclock time,
925 so other values used in the constructor are arbitrary and irrelevant.
928 TempoMetric const & metric (_points.front().metric());
929 const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, sc, Evoral::Beats(), Timecode::BBT_Time(), AudioTime);
930 TempoMapPoint::SuperClockComparator scmp;
932 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, scmp);
934 if (tmp != _points.begin()) {
941 TempoMapPoints::iterator
942 TempoMap::iterator_at (Evoral::Beats const & qn)
944 /* CALLER MUST HOLD LOCK */
946 if (_points.empty()) {
947 throw EmptyTempoMapException();
950 if (_points.size() == 1) {
951 return _points.begin();
954 /* Construct an arbitrary TempoMapPoint. The only property we care about is its quarters time,
955 so other values used in the constructor are arbitrary and irrelevant.
958 TempoMetric const & metric (_points.front().metric());
959 const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, 0, qn, Timecode::BBT_Time(), AudioTime);
960 TempoMapPoint::QuarterComparator bcmp;
962 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp);
964 if (tmp != _points.begin()) {
971 TempoMapPoints::iterator
972 TempoMap::iterator_at (Timecode::BBT_Time const & bbt)
974 /* CALLER MUST HOLD LOCK */
976 if (_points.empty()) {
977 throw EmptyTempoMapException();
980 if (_points.size() == 1) {
981 return _points.begin();
984 /* Construct an arbitrary TempoMapPoint. The only property we care about is its bbt time,
985 so other values used in the constructor are arbitrary and irrelevant.
988 TempoMetric const & metric (_points.front().metric());
989 const TempoMapPoint tp (TempoMapPoint::Flag(0), metric, metric, 0, Evoral::Beats(), bbt, MusicTime);
990 TempoMapPoint::BBTComparator bcmp;
992 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp);
994 if (tmp != _points.begin()) {
1002 TempoMap::bbt_at (superclock_t sc) const
1004 Glib::Threads::RWLock::ReaderLock lm (_lock);
1005 return bbt_at_locked (sc);
1009 TempoMap::bbt_at_locked (superclock_t sc) const
1011 TempoMapPoint point (const_point_at (sc));
1012 Evoral::Beats b ((sc - point.sclock()) / (double) point.metric().superclocks_per_quarter_note());
1013 return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, b.get_beats(), b.get_ticks()));
1017 TempoMap::bbt_at (Evoral::Beats const & qn) const
1019 Glib::Threads::RWLock::ReaderLock lm (_lock);
1020 return bbt_at_locked (qn);
1024 TempoMap::bbt_at_locked (Evoral::Beats const & qn) const
1026 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1027 TempoMapPoint const & point (const_point_at (qn));
1028 Evoral::Beats delta (qn - point.quarters());
1029 return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, delta.get_beats(), delta.get_ticks()));
1033 TempoMap::superclock_at (Evoral::Beats const & qn) const
1035 Glib::Threads::RWLock::ReaderLock lm (_lock);
1036 return superclock_at_locked (qn);
1040 TempoMap::superclock_at_locked (Evoral::Beats const & qn) const
1042 assert (!_points.empty());
1044 /* only the quarters property matters, since we're just using it to compare */
1045 const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, qn, Timecode::BBT_Time(), MusicTime);
1046 TempoMapPoint::QuarterComparator bcmp;
1048 TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp);
1050 if (i == _points.end()) {
1054 /* compute distance from reference point to b. Remember that Evoral::Beats is always interpreted as quarter notes */
1056 const Evoral::Beats q_delta = qn - i->quarters();
1057 superclock_t sclock_delta = i->metric().superclock_at_qn (q_delta);
1058 return i->sclock() + sclock_delta;
1062 TempoMap::superclock_at (Timecode::BBT_Time const & bbt) const
1064 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1065 return superclock_at_locked (bbt);
1069 TempoMap::superclock_at_locked (Timecode::BBT_Time const & bbt) const
1071 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1072 assert (!_points.empty());
1074 /* only the bbt property matters, since we're just using it to compare */
1075 const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, Evoral::Beats(), bbt, MusicTime);
1076 TempoMapPoint::BBTComparator bcmp;
1078 TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp);
1080 if (i == _points.end()) {
1084 /* this computes the distance from the point, in beats whose size is
1085 determined by the meter.
1088 const Timecode::BBT_Offset delta = i->metric().bbt_delta (bbt, i->bbt());
1090 /* convert to quarter notes */
1091 const int32_t ticks = delta.ticks + (Evoral::Beats::PPQN * delta.beats * 4) / i->metric().note_value();
1093 /* get distance in superclocks */
1094 return i->sclock() + i->metric().superclock_at_qn (Evoral::Beats::ticks (ticks));
1098 TempoMap::set_sample_rate (framecnt_t new_sr)
1100 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1101 double ratio = new_sr / (double) _sample_rate;
1103 for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) {
1104 i->map_reset_set_sclock_for_sr_change (llrint (ratio * i->sclock()));
1109 TempoMap::dump (std::ostream& ostr)
1111 Glib::Threads::RWLock::ReaderLock lm (_lock);
1116 TempoMap::dump_locked (std::ostream& ostr)
1118 ostr << "\n\n------------\n";
1119 for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) {
1120 ostr << *i << std::endl;
1125 TempoMap::remove_explicit_point (superclock_t sc)
1127 Glib::Threads::RWLock::WriterLock lm (_lock);
1128 TempoMapPoints::iterator p = iterator_at (sc);
1130 if (p->sclock() == sc) {
1136 TempoMap::move_to (superclock_t current, superclock_t destination, bool push)
1138 Glib::Threads::RWLock::WriterLock lm (_lock);
1139 TempoMapPoints::iterator p = iterator_at (current);
1141 if (p->sclock() != current) {
1142 cerr << "No point @ " << current << endl;
1146 /* put a "dirty" flag in at the nearest explicit point to the removal point
1149 if (p != _points.begin()) {
1150 TempoMapPoints::iterator prev (p);
1152 while (prev != _points.begin() && !prev->is_explicit()) {
1155 prev->set_dirty (true);
1157 TempoMapPoints::iterator nxt (p);
1159 while (nxt != _points.end() && !nxt->is_explicit()) {
1162 if (nxt != _points.end()) {
1163 nxt->set_dirty (true);
1167 if (!set_tempo_and_meter (p->metric(), p->metric(), destination, p->ramped(), true)) {
1173 p = iterator_at (destination);
1176 const superclock_t delta = destination - current;
1178 while (p != _points.end()) {
1179 p->set_sclock (p->sclock() + delta);
1190 TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, Evoral::Beats const & resolution)
1192 Glib::Threads::RWLock::ReaderLock lm (_lock);
1193 TempoMapPoints::iterator p = iterator_at (start);
1195 while (p != _points.end() && p->sclock() < start) {
1199 if (resolution == Evoral::Beats()) {
1200 /* just hand over the points as-is */
1201 while (p != _points.end() && p->sclock() < end) {
1208 superclock_t pos = p->sclock();
1210 TempoMapPoints::iterator nxt = p;
1213 while ((p != _points.end()) && (pos < end)) {
1214 /* recompute grid down to @param resolution level
1217 superclock_t sclock_delta = p->metric().superclock_at_qn (qpos);
1219 ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo),
1220 p->metric(), p->metric(),
1221 p->sclock() + sclock_delta,
1222 p->quarters() + qpos,
1223 p->metric().bbt_add (p->bbt(), Timecode::BBT_Offset (0, qpos.get_beats(), qpos.get_ticks())),
1228 pos += sclock_delta;
1230 if (pos >= nxt->sclock()) {
1239 TempoMap::get_bar_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, int32_t bar_gap)
1241 Glib::Threads::RWLock::ReaderLock lm (_lock);
1243 for (TempoMapPoints::iterator p = iterator_at (start); (p != _points.end()) && (p->sclock() < end); ++p) {
1245 if ((p->sclock() >= start) && (p->bbt().beats == 1) && ((p->bbt().bars == 1) || (p->bbt().bars % bar_gap == 0))) {
1246 ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo),
1247 p->metric(), p->metric(),
1258 operator<<(std::ostream& str, Meter const & m)
1260 return str << m.divisions_per_bar() << '/' << m.note_value();
1264 operator<<(std::ostream& str, Tempo const & t)
1266 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() << ')';
1270 operator<<(std::ostream& str, TempoMapPoint const & tmp)
1272 str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second
1273 << (tmp.is_explicit() ? " EXP" : " imp")
1274 << " qn " << tmp.quarters ()
1275 << " bbt " << tmp.bbt()
1276 << " lock to " << tmp.lock_style()
1279 if (tmp.is_explicit()) {
1280 str << " tempo " << *((Tempo*) &tmp.metric())
1281 << " meter " << *((Meter*) &tmp.metric())
1285 if (tmp.is_explicit() && tmp.ramped()) {
1286 str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter();
1293 #define SAMPLERATE 48000
1294 #define SECONDS_TO_SUPERCLOCK(s) (superclock_ticks_per_second * s)
1303 using namespace Timecode;
1306 BBT_Time b1 (1,1,1919);
1307 BBT_Time n1 (-1,1,1919);
1308 std::vector<Meter> meters;
1310 meters.push_back (Meter (4, 4));
1311 meters.push_back (Meter (5, 8));
1312 meters.push_back (Meter (11, 7));
1313 meters.push_back (Meter (3, 4));
1315 #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;
1317 for (std::vector<Meter>::iterator m = meters.begin(); m != meters.end(); ++m) {
1318 for (int B = 1; B < 4; ++B) {
1319 for (int b = 1; b < 13; ++b) {
1320 PRINT_RESULT((*m), "+", bbt_add, a, B, b, 0);
1321 PRINT_RESULT((*m), "+", bbt_add, a, B, b, 1);
1322 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN/2);
1323 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN);
1324 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN - 1);
1325 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN - 2);
1327 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 0);
1328 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 1);
1329 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN/2);
1330 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN);
1331 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN - 1);
1332 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN - 2);
1334 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 0);
1335 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 1);
1336 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN/2);
1337 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN);
1338 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN - 1);
1339 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN - 2);
1343 for (int B = 1; B < 4; ++B) {
1344 for (int b = 1; b < 13; ++b) {
1345 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 0);
1346 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 1);
1347 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN/2);
1348 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN);
1349 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN - 1);
1350 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN - 2);
1359 TempoMap tmap (Tempo (140), Meter (4,4), SAMPLERATE);
1364 tmap.set_tempo (Tempo (7), SECONDS_TO_SUPERCLOCK(7));
1365 tmap.set_tempo (Tempo (23), SECONDS_TO_SUPERCLOCK(23));
1366 tmap.set_tempo (Tempo (24), SECONDS_TO_SUPERCLOCK(24), true);
1367 tmap.set_tempo (Tempo (40), SECONDS_TO_SUPERCLOCK(28), true);
1368 tmap.set_tempo (Tempo (100), SECONDS_TO_SUPERCLOCK(100));
1369 tmap.set_tempo (Tempo (123), SECONDS_TO_SUPERCLOCK(23));
1371 tmap.set_meter (Meter (3, 4), SECONDS_TO_SUPERCLOCK(23));
1372 tmap.set_meter (Meter (5, 8), SECONDS_TO_SUPERCLOCK(100));
1373 tmap.set_meter (Meter (5, 7), SECONDS_TO_SUPERCLOCK(7));
1374 tmap.set_meter (Meter (4, 4), SECONDS_TO_SUPERCLOCK(24));
1375 tmap.set_meter (Meter (11, 7), SECONDS_TO_SUPERCLOCK(23));
1377 tmap.set_meter (Meter (3, 8), Timecode::BBT_Time (17, 1, 0));
1379 tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
1380 tmap.dump (std::cout);
1382 if (tmap.move_to (SECONDS_TO_SUPERCLOCK(23), SECONDS_TO_SUPERCLOCK (72))) {
1383 tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
1384 tmap.dump (std::cout);
1387 TempoMapPoints grid;
1388 tmap.get_grid (grid, SECONDS_TO_SUPERCLOCK (12.3), SECONDS_TO_SUPERCLOCK (44), Evoral::Beats::ticks ((Evoral::Beats::PPQN * 4) / 12));
1389 cout << "grid contains " << grid.size() << endl;
1390 for (TempoMapPoints::iterator p = grid.begin(); p != grid.end(); ++p) {
1394 TempoMapPoints bbt_grid;
1395 tmap.get_bar_grid (bbt_grid, SECONDS_TO_SUPERCLOCK (0), SECONDS_TO_SUPERCLOCK (100), 4);
1396 cout << "bbt_grid contains " << bbt_grid.size() << endl;
1397 for (TempoMapPoints::iterator p = bbt_grid.begin(); p != bbt_grid.end(); ++p) {