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, Temporal::Beats const & b) { return (sc * ((b.get_beats() * Temporal::Beats::PPQN) + b.get_ticks())) / Temporal::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 >= Temporal::Beats::PPQN) {
47 r.beats += r.ticks / Temporal::Beats::PPQN;
48 r.ticks %= Temporal::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 / Temporal::Beats::PPQN);
102 r.ticks = Temporal::Beats::PPQN + (r.ticks % Temporal::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 += Temporal::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 (samplecnt_t sr) const
159 return (superclock_ticks_per_second * Meter::note_value()) / (note_types_per_minute() / Tempo::note_type());
163 TempoMetric::superclocks_per_bar (samplecnt_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 (samplecnt_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 (samplecnt_t sr, superclock_t end_scpqn, Temporal::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 (Temporal::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 + Temporal::Beats ((sc - _sclock) / (double) (metric().superclocks_per_quarter_note ()));
335 return _quarters + Temporal::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 (Temporal::Beats const & qn) const
350 /* This TempoMapPoint must already have a fully computed metric and position */
352 Temporal::Beats quarters_delta = qn - _quarters;
353 int32_t ticks_delta = quarters_delta.to_ticks (Temporal::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, samplecnt_t sr)
361 TempoMapPoint tmp (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), initial_tempo, initial_meter, 0, Temporal::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 (Temporal::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 (Temporal::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 Temporal::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;
501 superclock_t first_dirty = 0;
503 for (tmp = _points.begin(); tmp != _points.end(); ) {
511 first_dirty = tmp->sclock();
514 TempoMapPoints::iterator next = tmp;
518 if (prev != _points.end()) {
519 if ((tmp->lock_style() == AudioTime)) {
520 /* audio-locked explicit point: recompute it's BBT and quarter-note position since this may have changed */
521 tmp->set_quarters (prev->quarters_at (tmp->sclock()));
522 if (static_cast<Meter>(tmp->metric()) != static_cast<Meter>(prev->metric())) {
523 /* new meter, must be on bar/measure start */
524 tmp->set_bbt (prev->metric().round_up_to_bar (prev->bbt_at (tmp->quarters())));
526 /* no meter change, tempo change required to be on beat */
527 tmp->set_bbt (prev->bbt_at (tmp->quarters()).round_up_to_beat());
532 superclock_t sc = tmp->sclock();
533 Temporal::Beats qn (tmp->quarters ());
534 Timecode::BBT_Time bbt (tmp->bbt());
535 const bool ramped = tmp->ramped () && next != _points.end();
537 /* Temporal::Beats are really quarter notes. This counts how many quarter notes
538 there are between grid points in this section of the tempo map.
540 const Temporal::Beats qn_step = (Temporal::Beats (1) * 4) / tmp->metric().note_value();
542 /* compute implicit points as far as the next explicit point, or limit,
543 whichever comes first.
546 const superclock_t sc_limit = (next == _points.end() ? limit : (*next).sclock());
550 /* define next beat in superclocks, beats and bbt */
553 bbt = tmp->metric().bbt_add (bbt, Timecode::BBT_Offset (0, 1, 0));
556 sc += tmp->metric().superclocks_per_note_type();
558 sc = tmp->sclock() + tmp->metric().superclock_at_qn (qn - tmp->quarters());
561 if (sc >= sc_limit) {
565 _points.insert (next, TempoMapPoint (*tmp, sc, qn, bbt));
568 tmp->set_dirty (false);
573 Changed (first_dirty, _points.back().sclock()); /* EMIT SIGNAL */
577 TempoMap::set_tempo_and_meter (Tempo const & tempo, Meter const & meter, superclock_t sc, bool ramp, bool flexible)
579 /* CALLER MUST HOLD LOCK */
581 assert (!_points.empty());
583 /* special case: first map entry is later than the new point */
585 if (_points.front().sclock() > sc) {
586 /* first point is later than sc. There's no iterator to reference a point at or before sc */
588 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
589 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
593 Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat();
594 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
596 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, b, bbt, AudioTime, ramp));
600 /* special case #3: only one map entry, at the same time as the new point.
601 This is the common case when editing tempo/meter in a session with a single tempo/meter
604 if (_points.size() == 1 && _points.front().sclock() == sc) {
606 *((Tempo*) &_points.front().metric()) = tempo;
607 *((Meter*) &_points.front().metric()) = meter;
608 _points.front().make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter));
612 /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */
614 TempoMapPoints::iterator i = iterator_at (sc);
615 TempoMapPoints::iterator nxt = i;
618 if (i->sclock() == sc) {
620 *((Tempo*) &i->metric()) = tempo;
621 *((Meter*) &i->metric()) = meter;
622 i->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter));
627 if (!flexible && (sc - i->sclock() < i->metric().superclocks_per_note_type())) {
628 cerr << "new tempo too close to previous ...\n";
632 TempoMapPoints::iterator e (i);
633 while (!e->is_explicit() && e != _points.begin()) {
637 if (e->metric().ramped()) {
638 /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it
639 and thus defines the new ramp distance.
641 e->compute_c_superclock (_sample_rate, tempo.superclocks_per_quarter_note (), sc);
644 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
645 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
649 Temporal::Beats qn = i->quarters_at (sc).round_to_beat();
651 /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc"
654 Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat ();
656 /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before"
659 if (i != _points.end()) {
663 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, qn, bbt, AudioTime, ramp));
668 TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp)
670 Glib::Threads::RWLock::WriterLock lm (_lock);
672 assert (!_points.empty());
674 /* special case: first map entry is later than the new point */
676 if (_points.front().sclock() > sc) {
677 /* first point is later than sc. There's no iterator to reference a point at or before sc */
679 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
680 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
684 Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat();
685 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
687 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, t, _points.front().metric(), sc, b, bbt, AudioTime, ramp));
691 /* special case #3: only one map entry, at the same time as the new point.
692 This is the common case when editing tempo/meter in a session with a single tempo/meter
695 if (_points.size() == 1 && _points.front().sclock() == sc) {
697 *((Tempo*) &_points.front().metric()) = t;
698 _points.front().make_explicit (TempoMapPoint::ExplicitTempo);
702 /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */
704 TempoMapPoints::iterator i = iterator_at (sc);
705 TempoMapPoints::iterator nxt = i;
708 if (i->sclock() == sc) {
710 *((Tempo*) &i->metric()) = t;
711 i->make_explicit (TempoMapPoint::ExplicitTempo);
716 if (sc - i->sclock() < i->metric().superclocks_per_note_type()) {
717 cerr << "new tempo too close to previous ...\n";
721 Meter const & meter (i->metric());
723 TempoMapPoints::iterator e (i);
724 while (!e->is_explicit() && e != _points.begin()) {
728 if (e->metric().ramped()) {
729 /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it
730 and thus defines the new ramp distance.
732 e->compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc);
735 /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
736 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
740 Temporal::Beats qn = i->quarters_at (sc).round_to_beat();
742 /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc"
745 Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat ();
747 /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before"
750 if (i != _points.end()) {
753 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, sc, qn, bbt, AudioTime, ramp));
758 TempoMap::set_tempo (Tempo const & t, Timecode::BBT_Time const & bbt, bool ramp)
760 Glib::Threads::RWLock::WriterLock lm (_lock);
762 /* tempo changes are required to be on-beat */
764 Timecode::BBT_Time on_beat = bbt.round_up_to_beat();
766 assert (!_points.empty());
768 if (_points.front().bbt() > on_beat) {
769 cerr << "Cannot insert tempo at " << bbt << " before first point at " << _points.front().bbt() << endl;
773 if (_points.size() == 1 && _points.front().bbt() == on_beat) {
775 *((Tempo*) &_points.front().metric()) = t;
776 _points.front().make_explicit (TempoMapPoint::ExplicitTempo);
780 TempoMapPoints::iterator i = iterator_at (on_beat);
782 if (i->bbt() == on_beat) {
783 *((Tempo*) &i->metric()) = t;
784 i->make_explicit (TempoMapPoint::ExplicitTempo);
788 Meter const & meter (i->metric());
791 /* stick a prototype music-locked point up front and let ::rebuild figure out the superclock and quarter time */
792 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, 0, Temporal::Beats(), on_beat, MusicTime, ramp));
797 TempoMap::set_meter (Meter const & m, Timecode::BBT_Time const & bbt)
799 Glib::Threads::RWLock::WriterLock lm (_lock);
800 Timecode::BBT_Time measure_start (m.round_up_to_bar (bbt));
802 assert (!_points.empty());
804 if (_points.front().bbt() > measure_start) {
805 cerr << "Cannot insert meter at " << bbt << " before first point at " << _points.front().bbt() << endl;
809 if (_points.size() == 1 && _points.front().bbt() == measure_start) {
811 *((Meter*) &_points.front().metric()) = m;
812 _points.front().make_explicit (TempoMapPoint::ExplicitMeter);
816 TempoMapPoints::iterator i = iterator_at (measure_start);
818 if (i->bbt() == measure_start) {
819 *((Meter*) &i->metric()) = m;
820 i->make_explicit (TempoMapPoint::ExplicitMeter);
824 Temporal::Beats qn = i->quarters_at (measure_start);
825 superclock_t sc = i->sclock() + i->metric().superclock_at_qn (qn);
827 Tempo const & tempo (i->metric());
830 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, qn, measure_start, MusicTime));
835 TempoMap::set_meter (Meter const & m, superclock_t sc)
837 Glib::Threads::RWLock::WriterLock lm (_lock);
838 assert (!_points.empty());
840 /* special case #2: first map entry is later than the new point */
842 if (_points.front().sclock() > sc) {
843 /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
844 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
848 Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat();
849 Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat ();
851 _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitMeter, _points.front().metric(), m, sc, b, bbt, AudioTime));
855 /* special case #3: only one map entry, at the same time as the new point.
857 This is the common case when editing tempo/meter in a session with a single tempo/meter
860 if (_points.size() == 1 && _points.front().sclock() == sc) {
862 *((Meter*) &_points.front().metric()) = m;
863 _points.front().make_explicit (TempoMapPoint::ExplicitMeter);
867 TempoMapPoints::iterator i = iterator_at (sc);
869 if (i->sclock() == sc) {
871 *((Meter*) &i->metric()) = m;
873 /* enforce rule described below regarding meter change positions */
875 if (i->bbt().beats != 1) {
876 i->set_bbt (Timecode::BBT_Time (i->bbt().bars + 1, 1, 0));
879 i->make_explicit (TempoMapPoint::ExplicitMeter);
884 if (sc - i->sclock() < i->metric().superclocks_per_note_type()) {
885 cerr << "new tempo too close to previous ...\n";
889 /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat,
890 even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not
894 Temporal::Beats b = i->quarters_at (sc).round_to_beat();
896 /* rule: all Meter changes must start a new measure. So determine the nearest, lower beat to "sc". If this is not
897 the first division of the measure, move to the next measure.
900 Timecode::BBT_Time bbt = i->bbt_at (b).round_down_to_beat ();
902 if (bbt.beats != 1) {
908 Tempo const & tempo (i->metric());
911 _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, b, bbt, AudioTime));
915 TempoMapPoints::iterator
916 TempoMap::iterator_at (superclock_t sc)
918 /* CALLER MUST HOLD LOCK */
920 if (_points.empty()) {
921 throw EmptyTempoMapException();
924 if (_points.size() == 1) {
925 return _points.begin();
928 /* Construct an arbitrary TempoMapPoint. The only property we care about is it's superclock time,
929 so other values used in the constructor are arbitrary and irrelevant.
932 TempoMetric const & metric (_points.front().metric());
933 const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, sc, Temporal::Beats(), Timecode::BBT_Time(), AudioTime);
934 TempoMapPoint::SuperClockComparator scmp;
936 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, scmp);
938 if (tmp != _points.begin()) {
945 TempoMapPoints::iterator
946 TempoMap::iterator_at (Temporal::Beats const & qn)
948 /* CALLER MUST HOLD LOCK */
950 if (_points.empty()) {
951 throw EmptyTempoMapException();
954 if (_points.size() == 1) {
955 return _points.begin();
958 /* Construct an arbitrary TempoMapPoint. The only property we care about is its quarters time,
959 so other values used in the constructor are arbitrary and irrelevant.
962 TempoMetric const & metric (_points.front().metric());
963 const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, 0, qn, Timecode::BBT_Time(), AudioTime);
964 TempoMapPoint::QuarterComparator bcmp;
966 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp);
968 if (tmp != _points.begin()) {
975 TempoMapPoints::iterator
976 TempoMap::iterator_at (Timecode::BBT_Time const & bbt)
978 /* CALLER MUST HOLD LOCK */
980 if (_points.empty()) {
981 throw EmptyTempoMapException();
984 if (_points.size() == 1) {
985 return _points.begin();
988 /* Construct an arbitrary TempoMapPoint. The only property we care about is its bbt time,
989 so other values used in the constructor are arbitrary and irrelevant.
992 TempoMetric const & metric (_points.front().metric());
993 const TempoMapPoint tp (TempoMapPoint::Flag(0), metric, metric, 0, Temporal::Beats(), bbt, MusicTime);
994 TempoMapPoint::BBTComparator bcmp;
996 TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp);
998 if (tmp != _points.begin()) {
1006 TempoMap::bbt_at (superclock_t sc) const
1008 Glib::Threads::RWLock::ReaderLock lm (_lock);
1009 return bbt_at_locked (sc);
1013 TempoMap::bbt_at_locked (superclock_t sc) const
1015 TempoMapPoint point (const_point_at (sc));
1016 Temporal::Beats b ((sc - point.sclock()) / (double) point.metric().superclocks_per_quarter_note());
1017 return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, b.get_beats(), b.get_ticks()));
1021 TempoMap::bbt_at (Temporal::Beats const & qn) const
1023 Glib::Threads::RWLock::ReaderLock lm (_lock);
1024 return bbt_at_locked (qn);
1028 TempoMap::bbt_at_locked (Temporal::Beats const & qn) const
1030 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1031 TempoMapPoint const & point (const_point_at (qn));
1032 Temporal::Beats delta (qn - point.quarters());
1033 return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, delta.get_beats(), delta.get_ticks()));
1037 TempoMap::superclock_at (Temporal::Beats const & qn) const
1039 Glib::Threads::RWLock::ReaderLock lm (_lock);
1040 return superclock_at_locked (qn);
1044 TempoMap::superclock_at_locked (Temporal::Beats const & qn) const
1046 assert (!_points.empty());
1048 /* only the quarters property matters, since we're just using it to compare */
1049 const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, qn, Timecode::BBT_Time(), MusicTime);
1050 TempoMapPoint::QuarterComparator bcmp;
1052 TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp);
1054 if (i == _points.end()) {
1058 /* compute distance from reference point to b. Remember that Temporal::Beats is always interpreted as quarter notes */
1060 const Temporal::Beats q_delta = qn - i->quarters();
1061 superclock_t sclock_delta = i->metric().superclock_at_qn (q_delta);
1062 return i->sclock() + sclock_delta;
1066 TempoMap::superclock_at (Timecode::BBT_Time const & bbt) const
1068 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1069 return superclock_at_locked (bbt);
1073 TempoMap::superclock_at_locked (Timecode::BBT_Time const & bbt) const
1075 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1076 assert (!_points.empty());
1078 /* only the bbt property matters, since we're just using it to compare */
1079 const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, Temporal::Beats(), bbt, MusicTime);
1080 TempoMapPoint::BBTComparator bcmp;
1082 TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp);
1084 if (i == _points.end()) {
1088 /* this computes the distance from the point, in beats whose size is
1089 determined by the meter.
1092 const Timecode::BBT_Offset delta = i->metric().bbt_delta (bbt, i->bbt());
1094 /* convert to quarter notes */
1095 const int32_t ticks = delta.ticks + (Temporal::Beats::PPQN * delta.beats * 4) / i->metric().note_value();
1097 /* get distance in superclocks */
1098 return i->sclock() + i->metric().superclock_at_qn (Temporal::Beats::ticks (ticks));
1102 TempoMap::set_sample_rate (samplecnt_t new_sr)
1104 //Glib::Threads::RWLock::ReaderLock lm (_lock);
1105 double ratio = new_sr / (double) _sample_rate;
1107 for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) {
1108 i->map_reset_set_sclock_for_sr_change (llrint (ratio * i->sclock()));
1113 TempoMap::dump (std::ostream& ostr)
1115 Glib::Threads::RWLock::ReaderLock lm (_lock);
1120 TempoMap::dump_locked (std::ostream& ostr)
1122 ostr << "\n\n------------\n";
1123 for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) {
1124 ostr << *i << std::endl;
1129 TempoMap::remove_explicit_point (superclock_t sc)
1131 Glib::Threads::RWLock::WriterLock lm (_lock);
1132 TempoMapPoints::iterator p = iterator_at (sc);
1134 if (p->sclock() == sc) {
1140 TempoMap::move_to (superclock_t current, superclock_t destination, bool push)
1142 Glib::Threads::RWLock::WriterLock lm (_lock);
1143 TempoMapPoints::iterator p = iterator_at (current);
1145 if (p->sclock() != current) {
1146 cerr << "No point @ " << current << endl;
1150 /* put a "dirty" flag in at the nearest explicit point to the removal point
1153 if (p != _points.begin()) {
1154 TempoMapPoints::iterator prev (p);
1156 while (prev != _points.begin() && !prev->is_explicit()) {
1159 prev->set_dirty (true);
1161 TempoMapPoints::iterator nxt (p);
1163 while (nxt != _points.end() && !nxt->is_explicit()) {
1166 if (nxt != _points.end()) {
1167 nxt->set_dirty (true);
1171 if (!set_tempo_and_meter (p->metric(), p->metric(), destination, p->ramped(), true)) {
1177 p = iterator_at (destination);
1180 const superclock_t delta = destination - current;
1182 while (p != _points.end()) {
1183 p->set_sclock (p->sclock() + delta);
1194 TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, Temporal::Beats const & resolution)
1196 Glib::Threads::RWLock::ReaderLock lm (_lock);
1197 TempoMapPoints::iterator p = iterator_at (start);
1199 while (p != _points.end() && p->sclock() < start) {
1203 if (resolution == Temporal::Beats()) {
1204 /* just hand over the points as-is */
1205 while (p != _points.end() && p->sclock() < end) {
1212 superclock_t pos = p->sclock();
1213 Temporal::Beats qpos;
1214 TempoMapPoints::iterator nxt = p;
1217 while ((p != _points.end()) && (pos < end)) {
1218 /* recompute grid down to @param resolution level
1221 superclock_t sclock_delta = p->metric().superclock_at_qn (qpos);
1223 ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo),
1224 p->metric(), p->metric(),
1225 p->sclock() + sclock_delta,
1226 p->quarters() + qpos,
1227 p->metric().bbt_add (p->bbt(), Timecode::BBT_Offset (0, qpos.get_beats(), qpos.get_ticks())),
1232 pos += sclock_delta;
1234 if (pos >= nxt->sclock()) {
1243 TempoMap::get_bar_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, int32_t bar_gap)
1245 Glib::Threads::RWLock::ReaderLock lm (_lock);
1247 for (TempoMapPoints::iterator p = iterator_at (start); (p != _points.end()) && (p->sclock() < end); ++p) {
1249 if ((p->sclock() >= start) && (p->bbt().beats == 1) && ((p->bbt().bars == 1) || (p->bbt().bars % bar_gap == 0))) {
1250 ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo),
1251 p->metric(), p->metric(),
1262 operator<<(std::ostream& str, Meter const & m)
1264 return str << m.divisions_per_bar() << '/' << m.note_value();
1268 operator<<(std::ostream& str, Tempo const & t)
1270 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() << ')';
1274 operator<<(std::ostream& str, TempoMapPoint const & tmp)
1276 str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second
1277 << (tmp.is_explicit() ? " EXP" : " imp")
1278 << " qn " << tmp.quarters ()
1279 << " bbt " << tmp.bbt()
1280 << " lock to " << tmp.lock_style()
1283 if (tmp.is_explicit()) {
1284 str << " tempo " << *((Tempo*) &tmp.metric())
1285 << " meter " << *((Meter*) &tmp.metric())
1289 if (tmp.is_explicit() && tmp.ramped()) {
1290 str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter();
1297 #define SAMPLERATE 48000
1298 #define SECONDS_TO_SUPERCLOCK(s) (superclock_ticks_per_second * s)
1307 using namespace Timecode;
1310 BBT_Time b1 (1,1,1919);
1311 BBT_Time n1 (-1,1,1919);
1312 std::vector<Meter> meters;
1314 meters.push_back (Meter (4, 4));
1315 meters.push_back (Meter (5, 8));
1316 meters.push_back (Meter (11, 7));
1317 meters.push_back (Meter (3, 4));
1319 #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;
1321 for (std::vector<Meter>::iterator m = meters.begin(); m != meters.end(); ++m) {
1322 for (int B = 1; B < 4; ++B) {
1323 for (int b = 1; b < 13; ++b) {
1324 PRINT_RESULT((*m), "+", bbt_add, a, B, b, 0);
1325 PRINT_RESULT((*m), "+", bbt_add, a, B, b, 1);
1326 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN/2);
1327 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN);
1328 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN - 1);
1329 PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN - 2);
1331 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 0);
1332 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 1);
1333 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN/2);
1334 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN);
1335 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN - 1);
1336 PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN - 2);
1338 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 0);
1339 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 1);
1340 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN/2);
1341 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN);
1342 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN - 1);
1343 PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN - 2);
1347 for (int B = 1; B < 4; ++B) {
1348 for (int b = 1; b < 13; ++b) {
1349 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 0);
1350 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 1);
1351 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN/2);
1352 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN);
1353 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN - 1);
1354 PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN - 2);
1363 TempoMap tmap (Tempo (140), Meter (4,4), SAMPLERATE);
1368 tmap.set_tempo (Tempo (7), SECONDS_TO_SUPERCLOCK(7));
1369 tmap.set_tempo (Tempo (23), SECONDS_TO_SUPERCLOCK(23));
1370 tmap.set_tempo (Tempo (24), SECONDS_TO_SUPERCLOCK(24), true);
1371 tmap.set_tempo (Tempo (40), SECONDS_TO_SUPERCLOCK(28), true);
1372 tmap.set_tempo (Tempo (100), SECONDS_TO_SUPERCLOCK(100));
1373 tmap.set_tempo (Tempo (123), SECONDS_TO_SUPERCLOCK(23));
1375 tmap.set_meter (Meter (3, 4), SECONDS_TO_SUPERCLOCK(23));
1376 tmap.set_meter (Meter (5, 8), SECONDS_TO_SUPERCLOCK(100));
1377 tmap.set_meter (Meter (5, 7), SECONDS_TO_SUPERCLOCK(7));
1378 tmap.set_meter (Meter (4, 4), SECONDS_TO_SUPERCLOCK(24));
1379 tmap.set_meter (Meter (11, 7), SECONDS_TO_SUPERCLOCK(23));
1381 tmap.set_meter (Meter (3, 8), Timecode::BBT_Time (17, 1, 0));
1383 tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
1384 tmap.dump (std::cout);
1386 if (tmap.move_to (SECONDS_TO_SUPERCLOCK(23), SECONDS_TO_SUPERCLOCK (72))) {
1387 tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
1388 tmap.dump (std::cout);
1391 TempoMapPoints grid;
1392 tmap.get_grid (grid, SECONDS_TO_SUPERCLOCK (12.3), SECONDS_TO_SUPERCLOCK (44), Temporal::Beats::ticks ((Temporal::Beats::PPQN * 4) / 12));
1393 cout << "grid contains " << grid.size() << endl;
1394 for (TempoMapPoints::iterator p = grid.begin(); p != grid.end(); ++p) {
1398 TempoMapPoints bbt_grid;
1399 tmap.get_bar_grid (bbt_grid, SECONDS_TO_SUPERCLOCK (0), SECONDS_TO_SUPERCLOCK (100), 4);
1400 cout << "bbt_grid contains " << bbt_grid.size() << endl;
1401 for (TempoMapPoints::iterator p = bbt_grid.begin(); p != bbt_grid.end(); ++p) {