X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=nutemp%2Ft.cc;h=296628445bb0601e1c354c7252e5dac8817afa67;hb=6b1b72a247bf04c81292fc41af4f69c9b7f90379;hp=6e4ee6a6e68259b905dea277a168fd1777ce40ad;hpb=73d0a849b8b88c68b011e7ac668eaf39cd1c9f48;p=ardour.git diff --git a/nutemp/t.cc b/nutemp/t.cc index 6e4ee6a6e6..296628445b 100644 --- a/nutemp/t.cc +++ b/nutemp/t.cc @@ -6,7 +6,7 @@ using std::cout; using std::endl; /* overloaded operator* that avoids floating point math when multiplying a superclock position by a number of quarter notes */ -superclock_t operator*(superclock_t sc, Evoral::Beats const & b) { return (sc * ((b.get_beats() * Evoral::Beats::PPQN) + b.get_ticks())) / Evoral::Beats::PPQN; } +superclock_t operator*(superclock_t sc, Temporal::Beats const & b) { return (sc * ((b.get_beats() * Temporal::Beats::PPQN) + b.get_ticks())) / Temporal::Beats::PPQN; } Timecode::BBT_Time Meter::bbt_add (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & add) const @@ -43,9 +43,9 @@ Meter::bbt_add (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & add Timecode::BBT_Offset r (bars + add.bars, beats + add.beats, ticks + add.ticks); - if (r.ticks >= Evoral::Beats::PPQN) { - r.beats += r.ticks / Evoral::Beats::PPQN; - r.ticks %= Evoral::Beats::PPQN; + if (r.ticks >= Temporal::Beats::PPQN) { + r.beats += r.ticks / Temporal::Beats::PPQN; + r.ticks %= Temporal::Beats::PPQN; } if (r.beats > _divisions_per_bar) { @@ -98,8 +98,8 @@ Meter::bbt_subtract (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const Timecode::BBT_Offset r (bars - sub.bars, beats - sub.beats, ticks - sub.ticks); if (r.ticks < 0) { - r.beats -= 1 - (r.ticks / Evoral::Beats::PPQN); - r.ticks = Evoral::Beats::PPQN + (r.ticks % Evoral::Beats::PPQN); + r.beats -= 1 - (r.ticks / Temporal::Beats::PPQN); + r.ticks = Temporal::Beats::PPQN + (r.ticks % Temporal::Beats::PPQN); } if (r.beats <= 0) { @@ -135,16 +135,14 @@ Meter::round_up_to_bar (Timecode::BBT_Time const & bbt) const return b; } -Evoral::Beats +Temporal::Beats Meter::to_quarters (Timecode::BBT_Offset const & offset) const { - Evoral::Beats b; + Temporal::Beats b; b += (offset.bars * _divisions_per_bar * 4) / _note_value; - cerr << offset.bars << " bars as quarters : " << b << " nv = " << (int) _note_value << endl; b += (offset.beats * 4) / _note_value; - cerr << offset.beats << " beats as quarters : " << (offset.beats * 4) / _note_value << " nv = " << (int) _note_value << endl; - b += Evoral::Beats::ticks (offset.ticks); + b += Temporal::Beats::ticks (offset.ticks); return b; } @@ -156,13 +154,13 @@ TempoMetric::superclock_per_note_type_at_superclock (superclock_t sc) const } superclock_t -TempoMetric::superclocks_per_grid (framecnt_t sr) const +TempoMetric::superclocks_per_grid (samplecnt_t sr) const { return (superclock_ticks_per_second * Meter::note_value()) / (note_types_per_minute() / Tempo::note_type()); } superclock_t -TempoMetric::superclocks_per_bar (framecnt_t sr) const +TempoMetric::superclocks_per_bar (samplecnt_t sr) const { return superclocks_per_grid (sr) * _divisions_per_bar; } @@ -277,7 +275,7 @@ It would be more accurate to substitute the work 'pulse' for 'beat' above. */ void -TempoMetric::compute_c_superclock (framecnt_t sr, superclock_t end_scpqn, superclock_t superclock_duration) +TempoMetric::compute_c_superclock (samplecnt_t sr, superclock_t end_scpqn, superclock_t superclock_duration) { if ((superclocks_per_quarter_note() == end_scpqn) || !ramped()) { _c_per_superclock = 0.0; @@ -287,7 +285,7 @@ TempoMetric::compute_c_superclock (framecnt_t sr, superclock_t end_scpqn, superc _c_per_superclock = log ((double) superclocks_per_quarter_note () / end_scpqn) / superclock_duration; } void -TempoMetric::compute_c_quarters (framecnt_t sr, superclock_t end_scpqn, Evoral::Beats const & quarter_duration) +TempoMetric::compute_c_quarters (samplecnt_t sr, superclock_t end_scpqn, Temporal::Beats const & quarter_duration) { if ((superclocks_per_quarter_note () == end_scpqn) || !ramped()) { _c_per_quarter = 0.0; @@ -298,7 +296,7 @@ TempoMetric::compute_c_quarters (framecnt_t sr, superclock_t end_scpqn, Evoral:: } superclock_t -TempoMetric::superclock_at_qn (Evoral::Beats const & qn) const +TempoMetric::superclock_at_qn (Temporal::Beats const & qn) const { if (_c_per_quarter == 0.0) { /* not ramped, use linear */ @@ -308,45 +306,68 @@ TempoMetric::superclock_at_qn (Evoral::Beats const & qn) const return llrint (superclocks_per_quarter_note() * (log1p (_c_per_quarter * qn.to_double()) / _c_per_quarter)); } -Evoral::Beats +void +TempoMapPoint::set_map (TempoMap* m) +{ + _map = m; +} + +void +TempoMapPoint::set_dirty (bool yn) +{ + if (yn != _dirty) { + _dirty = yn; + if (yn && _map) { + _map->set_dirty (true); + } + } +} + +Temporal::Beats TempoMapPoint::quarters_at (superclock_t sc) const { /* This TempoMapPoint must already have a fully computed metric and position */ if (!ramped()) { - return _quarters + Evoral::Beats ((sc - _sclock) / (double) (metric().superclocks_per_quarter_note ())); + return _quarters + Temporal::Beats ((sc - _sclock) / (double) (metric().superclocks_per_quarter_note ())); } - return _quarters + Evoral::Beats (expm1 (metric().c_per_superclock() * (sc - _sclock)) / (metric().c_per_superclock() * metric().superclocks_per_quarter_note ())); + return _quarters + Temporal::Beats (expm1 (metric().c_per_superclock() * (sc - _sclock)) / (metric().c_per_superclock() * metric().superclocks_per_quarter_note ())); } -Evoral::Beats +Temporal::Beats TempoMapPoint::quarters_at (Timecode::BBT_Time const & bbt) const { /* This TempoMapPoint must already have a fully computed metric and position */ Timecode::BBT_Offset offset = metric().bbt_delta (bbt, _bbt); - cerr << "QA BBT DELTA between " << bbt << " and " << _bbt << " = " << offset << " as quarters for " << static_cast (metric()) << " = " << metric().to_quarters (offset) << endl; return _quarters + metric().to_quarters (offset); } Timecode::BBT_Time -TempoMapPoint::bbt_at (Evoral::Beats const & qn) const +TempoMapPoint::bbt_at (Temporal::Beats const & qn) const { /* This TempoMapPoint must already have a fully computed metric and position */ - Evoral::Beats quarters_delta = qn - _quarters; - int32_t ticks_delta = quarters_delta.to_ticks (Evoral::Beats::PPQN); + Temporal::Beats quarters_delta = qn - _quarters; + int32_t ticks_delta = quarters_delta.to_ticks (Temporal::Beats::PPQN); return metric().bbt_add (_bbt, Timecode::BBT_Offset (0, 0, ticks_delta)); } -TempoMap::TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, framecnt_t sr) +TempoMap::TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, samplecnt_t sr) : _sample_rate (sr) + , _dirty (false) { - TempoMapPoint tmp (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), initial_tempo, initial_meter, 0, Evoral::Beats(), Timecode::BBT_Time(), AudioTime); + TempoMapPoint tmp (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), initial_tempo, initial_meter, 0, Temporal::Beats(), Timecode::BBT_Time(), AudioTime); _points.push_back (tmp); } +void +TempoMap::set_dirty (bool yn) +{ + _dirty = yn; +} + Meter const & TempoMap::meter_at (superclock_t sc) const { @@ -355,7 +376,7 @@ TempoMap::meter_at (superclock_t sc) const } Meter const & -TempoMap::meter_at (Evoral::Beats const & b) const +TempoMap::meter_at (Temporal::Beats const & b) const { Glib::Threads::RWLock::ReaderLock lm (_lock); return meter_at_locked (b); @@ -376,7 +397,7 @@ TempoMap::tempo_at (superclock_t sc) const } Tempo const & -TempoMap::tempo_at (Evoral::Beats const &b) const +TempoMap::tempo_at (Temporal::Beats const &b) const { Glib::Threads::RWLock::ReaderLock lm (_lock); return tempo_at_locked (b); @@ -393,12 +414,7 @@ void TempoMap::rebuild (superclock_t limit) { Glib::Threads::RWLock::WriterLock lm (_lock); - rebuild_locked (limit); -} -void -TempoMap::rebuild_locked (superclock_t limit) -{ /* step one: remove all implicit points after a dirty explicit point */ restart: @@ -411,6 +427,11 @@ TempoMap::rebuild_locked (superclock_t limit) first_explicit_dirty = tmp; + if (first_explicit_dirty == _points.end()) { + /* nothing is dirty */ + return; + } + /* remove all implicit points, because we're going to recalculate them all */ while (tmp != _points.end()) { @@ -424,14 +445,14 @@ TempoMap::rebuild_locked (superclock_t limit) tmp = next; } - /* compute C for all ramped sections */ + /* compute C-by-quarters for all ramped sections, because we need it shortly */ for (tmp = first_explicit_dirty; tmp != _points.end(); ) { TempoMapPoints::iterator nxt = tmp; ++nxt; if (tmp->ramped() && (nxt != _points.end())) { - tmp->metric().compute_c_quarters (_sample_rate, nxt->metric().superclocks_per_quarter_note (), nxt->quarters() - tmp->quarters()); + tmp->compute_c_quarters (_sample_rate, nxt->metric().superclocks_per_quarter_note (), nxt->quarters() - tmp->quarters()); } tmp = nxt; @@ -450,22 +471,13 @@ TempoMap::rebuild_locked (superclock_t limit) if ((tmp->lock_style() == MusicTime)) { /* determine superclock and quarter note time for this (music-time) locked point */ - cerr << "MT-lock, prev = " << *prev << endl; - - Evoral::Beats qn = prev->quarters_at (tmp->bbt()); - cerr << "MT-lock @ " << tmp->bbt() << " => " << qn << endl; + Temporal::Beats qn = prev->quarters_at (tmp->bbt()); superclock_t sc = prev->sclock() + prev->metric().superclock_at_qn (qn - prev->quarters()); - cerr << "MT-lock sc is " << prev->metric().superclock_at_qn (qn - prev->quarters()) << " after " << prev->sclock() << " = " << sc - << " secs = " << prev->metric().superclock_at_qn (qn - prev->quarters()) / (double) superclock_ticks_per_second - << endl; if (qn != tmp->quarters() || tmp->sclock() != sc) { - cerr << "Ned to move " << *tmp << endl; tmp->set_quarters (qn); tmp->set_sclock (sc); - cerr << "using " << *prev << " moved music-time-locked @ " << tmp->bbt() << " to " << sc << " aka " << qn << endl; _points.sort (TempoMapPoint::SuperClockComparator()); - cerr << "Restart\n"; goto restart; } } @@ -479,32 +491,34 @@ TempoMap::rebuild_locked (superclock_t limit) * of music-time locked points. */ - cerr << "POST-SORT\n"; - dump_locked (cerr); - prev = _points.end(); /* step two: add new implicit points between each pair of explicit * points, after the dirty explicit point */ + bool hit_dirty = false; + superclock_t first_dirty = 0; + for (tmp = _points.begin(); tmp != _points.end(); ) { - if (!tmp->dirty()) { - ++tmp; - continue; + if (!hit_dirty) { + if (!tmp->dirty()) { + ++tmp; + continue; + } + hit_dirty = true; + first_dirty = tmp->sclock(); } TempoMapPoints::iterator next = tmp; ++next; + if (prev != _points.end()) { if ((tmp->lock_style() == AudioTime)) { - cerr << "AT: check " << *tmp << endl - << "\t\tusing " << *prev << endl; /* audio-locked explicit point: recompute it's BBT and quarter-note position since this may have changed */ tmp->set_quarters (prev->quarters_at (tmp->sclock())); - cerr << "AT - recompute quarters at " << tmp->quarters () << endl; if (static_cast(tmp->metric()) != static_cast(prev->metric())) { /* new meter, must be on bar/measure start */ tmp->set_bbt (prev->metric().round_up_to_bar (prev->bbt_at (tmp->quarters()))); @@ -512,19 +526,18 @@ TempoMap::rebuild_locked (superclock_t limit) /* no meter change, tempo change required to be on beat */ tmp->set_bbt (prev->bbt_at (tmp->quarters()).round_up_to_beat()); } - cerr << "AT - recompute bbt at " << tmp->bbt () << endl; } } superclock_t sc = tmp->sclock(); - Evoral::Beats qn (tmp->quarters ()); + Temporal::Beats qn (tmp->quarters ()); Timecode::BBT_Time bbt (tmp->bbt()); const bool ramped = tmp->ramped () && next != _points.end(); - /* Evoral::Beats are really quarter notes. This counts how many quarter notes + /* Temporal::Beats are really quarter notes. This counts how many quarter notes there are between grid points in this section of the tempo map. */ - const Evoral::Beats qn_step = (Evoral::Beats (1) * 4) / tmp->metric().note_value(); + const Temporal::Beats qn_step = (Temporal::Beats (1) * 4) / tmp->metric().note_value(); /* compute implicit points as far as the next explicit point, or limit, whichever comes first. @@ -552,10 +565,103 @@ TempoMap::rebuild_locked (superclock_t limit) _points.insert (next, TempoMapPoint (*tmp, sc, qn, bbt)); } - (*tmp).set_dirty (false); + tmp->set_dirty (false); prev = tmp; tmp = next; } + + Changed (first_dirty, _points.back().sclock()); /* EMIT SIGNAL */ +} + +bool +TempoMap::set_tempo_and_meter (Tempo const & tempo, Meter const & meter, superclock_t sc, bool ramp, bool flexible) +{ + /* CALLER MUST HOLD LOCK */ + + assert (!_points.empty()); + + /* special case: first map entry is later than the new point */ + + if (_points.front().sclock() > sc) { + /* first point is later than sc. There's no iterator to reference a point at or before sc */ + + /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, + even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not + fractional. + */ + + Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat(); + Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat (); + + _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, b, bbt, AudioTime, ramp)); + return true; + } + + /* special case #3: only one map entry, at the same time as the new point. + This is the common case when editing tempo/meter in a session with a single tempo/meter + */ + + if (_points.size() == 1 && _points.front().sclock() == sc) { + /* change metrics */ + *((Tempo*) &_points.front().metric()) = tempo; + *((Meter*) &_points.front().metric()) = meter; + _points.front().make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter)); + return true; + } + + /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */ + + TempoMapPoints::iterator i = iterator_at (sc); + TempoMapPoints::iterator nxt = i; + ++nxt; + + if (i->sclock() == sc) { + /* change metrics */ + *((Tempo*) &i->metric()) = tempo; + *((Meter*) &i->metric()) = meter; + i->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter)); + /* done */ + return true; + } + + if (!flexible && (sc - i->sclock() < i->metric().superclocks_per_note_type())) { + cerr << "new tempo too close to previous ...\n"; + return false; + } + + TempoMapPoints::iterator e (i); + while (!e->is_explicit() && e != _points.begin()) { + --e; + } + + if (e->metric().ramped()) { + /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it + and thus defines the new ramp distance. + */ + e->compute_c_superclock (_sample_rate, tempo.superclocks_per_quarter_note (), sc); + } + + /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, + even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not + fractional. + */ + + Temporal::Beats qn = i->quarters_at (sc).round_to_beat(); + + /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc" + */ + + Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat (); + + /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before" + */ + + if (i != _points.end()) { + ++i; + } + + _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, qn, bbt, AudioTime, ramp)); + return true; } bool @@ -575,7 +681,7 @@ TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp) fractional. */ - Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat(); + Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat(); Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat (); _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, t, _points.front().metric(), sc, b, bbt, AudioTime, ramp)); @@ -614,11 +720,16 @@ TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp) Meter const & meter (i->metric()); - if (i->metric().ramped()) { + TempoMapPoints::iterator e (i); + while (!e->is_explicit() && e != _points.begin()) { + --e; + } + + if (e->metric().ramped()) { /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it and thus defines the new ramp distance. */ - i->metric().compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc); + e->compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc); } /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, @@ -626,7 +737,7 @@ TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp) fractional. */ - Evoral::Beats qn = i->quarters_at (sc).round_to_beat(); + Temporal::Beats qn = i->quarters_at (sc).round_to_beat(); /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc" */ @@ -652,8 +763,6 @@ TempoMap::set_tempo (Tempo const & t, Timecode::BBT_Time const & bbt, bool ramp) Timecode::BBT_Time on_beat = bbt.round_up_to_beat(); - cerr << "Try to set tempo @ " << on_beat << " to " << t << endl; - assert (!_points.empty()); if (_points.front().bbt() > on_beat) { @@ -680,7 +789,7 @@ TempoMap::set_tempo (Tempo const & t, Timecode::BBT_Time const & bbt, bool ramp) ++i; /* stick a prototype music-locked point up front and let ::rebuild figure out the superclock and quarter time */ - _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, 0, Evoral::Beats(), on_beat, MusicTime, ramp)); + _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, 0, Temporal::Beats(), on_beat, MusicTime, ramp)); return true; } @@ -690,8 +799,6 @@ TempoMap::set_meter (Meter const & m, Timecode::BBT_Time const & bbt) Glib::Threads::RWLock::WriterLock lm (_lock); Timecode::BBT_Time measure_start (m.round_up_to_bar (bbt)); - cerr << "Try to set meter @ " << measure_start << " to " << m << endl; - assert (!_points.empty()); if (_points.front().bbt() > measure_start) { @@ -701,9 +808,7 @@ TempoMap::set_meter (Meter const & m, Timecode::BBT_Time const & bbt) if (_points.size() == 1 && _points.front().bbt() == measure_start) { /* change Meter */ - cerr << "Found the single point\n"; *((Meter*) &_points.front().metric()) = m; - cerr << "Updated meter to " << m << endl; _points.front().make_explicit (TempoMapPoint::ExplicitMeter); return true; } @@ -712,21 +817,16 @@ TempoMap::set_meter (Meter const & m, Timecode::BBT_Time const & bbt) if (i->bbt() == measure_start) { *((Meter*) &i->metric()) = m; - cerr << "Updated meter to " << m << endl; i->make_explicit (TempoMapPoint::ExplicitMeter); return true; } - Evoral::Beats qn = i->quarters_at (measure_start); + Temporal::Beats qn = i->quarters_at (measure_start); superclock_t sc = i->sclock() + i->metric().superclock_at_qn (qn); Tempo const & tempo (i->metric()); ++i; - cerr << "NEW METER, provisionally @ " - << TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, qn, measure_start, MusicTime) - << endl; - _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, qn, measure_start, MusicTime)); return true; } @@ -745,7 +845,7 @@ TempoMap::set_meter (Meter const & m, superclock_t sc) fractional. */ - Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat(); + Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat(); Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat (); _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitMeter, _points.front().metric(), m, sc, b, bbt, AudioTime)); @@ -791,7 +891,7 @@ TempoMap::set_meter (Meter const & m, superclock_t sc) fractional. */ - Evoral::Beats b = i->quarters_at (sc).round_to_beat(); + Temporal::Beats b = i->quarters_at (sc).round_to_beat(); /* rule: all Meter changes must start a new measure. So determine the nearest, lower beat to "sc". If this is not the first division of the measure, move to the next measure. @@ -830,7 +930,7 @@ TempoMap::iterator_at (superclock_t sc) */ TempoMetric const & metric (_points.front().metric()); - const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, sc, Evoral::Beats(), Timecode::BBT_Time(), AudioTime); + const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, sc, Temporal::Beats(), Timecode::BBT_Time(), AudioTime); TempoMapPoint::SuperClockComparator scmp; TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, scmp); @@ -843,7 +943,7 @@ TempoMap::iterator_at (superclock_t sc) } TempoMapPoints::iterator -TempoMap::iterator_at (Evoral::Beats const & qn) +TempoMap::iterator_at (Temporal::Beats const & qn) { /* CALLER MUST HOLD LOCK */ @@ -890,7 +990,7 @@ TempoMap::iterator_at (Timecode::BBT_Time const & bbt) */ TempoMetric const & metric (_points.front().metric()); - const TempoMapPoint tp (TempoMapPoint::Flag(0), metric, metric, 0, Evoral::Beats(), bbt, MusicTime); + const TempoMapPoint tp (TempoMapPoint::Flag(0), metric, metric, 0, Temporal::Beats(), bbt, MusicTime); TempoMapPoint::BBTComparator bcmp; TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp); @@ -913,66 +1013,35 @@ Timecode::BBT_Time TempoMap::bbt_at_locked (superclock_t sc) const { TempoMapPoint point (const_point_at (sc)); - Evoral::Beats b ((sc - point.sclock()) / (double) point.metric().superclocks_per_quarter_note()); + Temporal::Beats b ((sc - point.sclock()) / (double) point.metric().superclocks_per_quarter_note()); return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, b.get_beats(), b.get_ticks())); } Timecode::BBT_Time -TempoMap::bbt_at (Evoral::Beats const & qn) const +TempoMap::bbt_at (Temporal::Beats const & qn) const { Glib::Threads::RWLock::ReaderLock lm (_lock); return bbt_at_locked (qn); } Timecode::BBT_Time -TempoMap::bbt_at_locked (Evoral::Beats const & qn) const +TempoMap::bbt_at_locked (Temporal::Beats const & qn) const { //Glib::Threads::RWLock::ReaderLock lm (_lock); TempoMapPoint const & point (const_point_at (qn)); - Evoral::Beats delta (qn - point.quarters()); + Temporal::Beats delta (qn - point.quarters()); return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, delta.get_beats(), delta.get_ticks())); } -Evoral::Beats -TempoMap::quarter_note_at (superclock_t sc) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return quarter_note_at_locked (sc); -} - -Evoral::Beats -TempoMap::quarter_note_at_locked (superclock_t sc) const -{ - return const_point_at (sc).quarters_at (sc); -} - -Evoral::Beats -TempoMap::quarter_note_at (Timecode::BBT_Time const & bbt) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return quarter_note_at_locked (bbt); -} - -Evoral::Beats -TempoMap::quarter_note_at_locked (Timecode::BBT_Time const & bbt) const -{ - //Glib::Threads::RWLock::ReaderLock lm (_lock); - TempoMapPoint const & point (const_point_at (bbt)); - - Timecode::BBT_Time bbt_delta (point.metric().bbt_subtract (bbt, point.bbt())); - /* XXX need to convert the metric division to quarters to match Evoral::Beats == Evoral::Quarters */ - return point.quarters() + Evoral::Beats ((point.metric().divisions_per_bar() * bbt_delta.bars) + bbt_delta.beats, bbt_delta.ticks); -} - superclock_t -TempoMap::superclock_at (Evoral::Beats const & qn) const +TempoMap::superclock_at (Temporal::Beats const & qn) const { Glib::Threads::RWLock::ReaderLock lm (_lock); return superclock_at_locked (qn); } superclock_t -TempoMap::superclock_at_locked (Evoral::Beats const & qn) const +TempoMap::superclock_at_locked (Temporal::Beats const & qn) const { assert (!_points.empty()); @@ -986,10 +1055,10 @@ TempoMap::superclock_at_locked (Evoral::Beats const & qn) const --i; } - /* compute distance from reference point to b. Remember that Evoral::Beats is always interpreted as quarter notes */ + /* compute distance from reference point to b. Remember that Temporal::Beats is always interpreted as quarter notes */ - const Evoral::Beats q_delta = qn - i->quarters(); - superclock_t sclock_delta = i->metric().superclocks_per_quarter_note () * q_delta; + const Temporal::Beats q_delta = qn - i->quarters(); + superclock_t sclock_delta = i->metric().superclock_at_qn (q_delta); return i->sclock() + sclock_delta; } @@ -1007,7 +1076,7 @@ TempoMap::superclock_at_locked (Timecode::BBT_Time const & bbt) const assert (!_points.empty()); /* only the bbt property matters, since we're just using it to compare */ - const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, Evoral::Beats(), bbt, MusicTime); + const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, Temporal::Beats(), bbt, MusicTime); TempoMapPoint::BBTComparator bcmp; TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp); @@ -1016,15 +1085,21 @@ TempoMap::superclock_at_locked (Timecode::BBT_Time const & bbt) const --i; } - /* compute distance from reference point to b. Remember that Evoral::Beats is always interpreted as quarter notes */ + /* this computes the distance from the point, in beats whose size is + determined by the meter. + */ - //const Evoral::Beats delta = b - i->beats(); - //return i->sclock() + samples_to_superclock ((delta / i->metric().quarter_notes_per_minute ()).to_double() * _sample_rate, _sample_rate); - return 0; + const Timecode::BBT_Offset delta = i->metric().bbt_delta (bbt, i->bbt()); + + /* convert to quarter notes */ + const int32_t ticks = delta.ticks + (Temporal::Beats::PPQN * delta.beats * 4) / i->metric().note_value(); + + /* get distance in superclocks */ + return i->sclock() + i->metric().superclock_at_qn (Temporal::Beats::ticks (ticks)); } void -TempoMap::set_sample_rate (framecnt_t new_sr) +TempoMap::set_sample_rate (samplecnt_t new_sr) { //Glib::Threads::RWLock::ReaderLock lm (_lock); double ratio = new_sr / (double) _sample_rate; @@ -1042,7 +1117,7 @@ TempoMap::dump (std::ostream& ostr) } void -TempoMap::dump (std::ostream& ostr) +TempoMap::dump_locked (std::ostream& ostr) { ostr << "\n\n------------\n"; for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) { @@ -1053,7 +1128,7 @@ TempoMap::dump (std::ostream& ostr) void TempoMap::remove_explicit_point (superclock_t sc) { - //Glib::Threads::RWLock::WriterLock lm (_lock); + Glib::Threads::RWLock::WriterLock lm (_lock); TempoMapPoints::iterator p = iterator_at (sc); if (p->sclock() == sc) { @@ -1061,64 +1136,160 @@ TempoMap::remove_explicit_point (superclock_t sc) } } -void -TempoMap::move_explicit (superclock_t current, superclock_t destination) +bool +TempoMap::move_to (superclock_t current, superclock_t destination, bool push) { - //Glib::Threads::RWLock::WriterLock lm (_lock); + Glib::Threads::RWLock::WriterLock lm (_lock); TempoMapPoints::iterator p = iterator_at (current); if (p->sclock() != current) { - return; + cerr << "No point @ " << current << endl; + return false; + } + + /* put a "dirty" flag in at the nearest explicit point to the removal point + */ + + if (p != _points.begin()) { + TempoMapPoints::iterator prev (p); + --prev; + while (prev != _points.begin() && !prev->is_explicit()) { + --prev; + } + prev->set_dirty (true); + } else { + TempoMapPoints::iterator nxt (p); + ++nxt; + while (nxt != _points.end() && !nxt->is_explicit()) { + ++nxt; + } + if (nxt != _points.end()) { + nxt->set_dirty (true); + } + } + + if (!set_tempo_and_meter (p->metric(), p->metric(), destination, p->ramped(), true)) { + return false; + } + + if (push) { + + p = iterator_at (destination); + ++p; + + const superclock_t delta = destination - current; + + while (p != _points.end()) { + p->set_sclock (p->sclock() + delta); + ++p; + } } - move_explicit_to (p, destination); + _points.erase (p); + + return true; } void -TempoMap::move_explicit_to (TempoMapPoints::iterator p, superclock_t destination) +TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, Temporal::Beats const & resolution) { - /* CALLER MUST HOLD LOCK */ - - TempoMapPoint point (*p); - point.set_sclock (destination); + Glib::Threads::RWLock::ReaderLock lm (_lock); + TempoMapPoints::iterator p = iterator_at (start); - TempoMapPoints::iterator prev; + while (p != _points.end() && p->sclock() < start) { + ++p; + } - prev = p; - if (p != _points.begin()) { - --prev; + if (resolution == Temporal::Beats()) { + /* just hand over the points as-is */ + while (p != _points.end() && p->sclock() < end) { + ret.push_back (*p); + ++p; + } + return; } - /* remove existing */ + superclock_t pos = p->sclock(); + Temporal::Beats qpos; + TempoMapPoints::iterator nxt = p; + ++nxt; - _points.erase (p); - prev->set_dirty (true); + while ((p != _points.end()) && (pos < end)) { + /* recompute grid down to @param resolution level + */ - /* find insertion point */ + superclock_t sclock_delta = p->metric().superclock_at_qn (qpos); - p = iterator_at (destination); + ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), + p->metric(), p->metric(), + p->sclock() + sclock_delta, + p->quarters() + qpos, + p->metric().bbt_add (p->bbt(), Timecode::BBT_Offset (0, qpos.get_beats(), qpos.get_ticks())), + AudioTime, + p->ramped())); - /* STL insert semantics are always "insert-before", whereas ::iterator_at() returns iterator-at-or-before */ - ++p; + qpos += resolution; + pos += sclock_delta; - _points.insert (p, point); + if (pos >= nxt->sclock()) { + p = nxt; + ++nxt; + } + } } + void -TempoMap::move_implicit (superclock_t current, superclock_t destination) +TempoMap::get_bar_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, int32_t bar_gap) { - //Glib::Threads::RWLock::WriterLock lm (_lock); - TempoMapPoints::iterator p = iterator_at (current); + Glib::Threads::RWLock::ReaderLock lm (_lock); - if (p->sclock() != current) { - return; + for (TempoMapPoints::iterator p = iterator_at (start); (p != _points.end()) && (p->sclock() < end); ++p) { + + if ((p->sclock() >= start) && (p->bbt().beats == 1) && ((p->bbt().bars == 1) || (p->bbt().bars % bar_gap == 0))) { + ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), + p->metric(), p->metric(), + p->sclock(), + p->quarters(), + p->bbt(), + AudioTime, + p->ramped())); + } } +} + +std::ostream& +operator<<(std::ostream& str, Meter const & m) +{ + return str << m.divisions_per_bar() << '/' << m.note_value(); +} + +std::ostream& +operator<<(std::ostream& str, Tempo const & t) +{ + return str << t.note_types_per_minute() << " 1/" << t.note_type() << " notes per minute (" << t.superclocks_per_note_type() << " sc-per-1/" << t.note_type() << ')'; +} + +std::ostream& +operator<<(std::ostream& str, TempoMapPoint const & tmp) +{ + str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second + << (tmp.is_explicit() ? " EXP" : " imp") + << " qn " << tmp.quarters () + << " bbt " << tmp.bbt() + << " lock to " << tmp.lock_style() + ; - if (p->is_implicit()) { - p->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo)); + if (tmp.is_explicit()) { + str << " tempo " << *((Tempo*) &tmp.metric()) + << " meter " << *((Meter*) &tmp.metric()) + ; } - move_explicit_to (p, destination); + if (tmp.is_explicit() && tmp.ramped()) { + str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter(); + } + return str; } /*******/ @@ -1152,24 +1323,24 @@ test_bbt_math () for (int b = 1; b < 13; ++b) { PRINT_RESULT((*m), "+", bbt_add, a, B, b, 0); PRINT_RESULT((*m), "+", bbt_add, a, B, b, 1); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN/2); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN - 1); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Evoral::Beats::PPQN - 2); + PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN/2); + PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN); + PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN - 1); + PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN - 2); PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 0); PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 1); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN/2); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN - 1); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Evoral::Beats::PPQN - 2); + PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN/2); + PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN); + PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN - 1); + PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN - 2); PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 0); PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 1); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN/2); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN - 1); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Evoral::Beats::PPQN - 2); + PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN/2); + PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN); + PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN - 1); + PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN - 2); } } @@ -1177,10 +1348,10 @@ test_bbt_math () for (int b = 1; b < 13; ++b) { PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 0); PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 1); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN/2); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN - 1); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Evoral::Beats::PPQN - 2); + PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN/2); + PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN); + PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN - 1); + PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN - 2); } } } @@ -1212,39 +1383,24 @@ main () tmap.rebuild (SECONDS_TO_SUPERCLOCK (120)); tmap.dump (std::cout); - return 0; -} - -std::ostream& -operator<<(std::ostream& str, Meter const & m) -{ - return str << m.divisions_per_bar() << '/' << m.note_value(); -} - -std::ostream& -operator<<(std::ostream& str, Tempo const & t) -{ - return str << t.note_types_per_minute() << " 1/" << t.note_type() << " notes per minute (" << t.superclocks_per_note_type() << " sc-per-1/" << t.note_type() << ')'; -} - -std::ostream& -operator<<(std::ostream& str, TempoMapPoint const & tmp) -{ - str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second - << (tmp.is_explicit() ? " EXP" : " imp") - << " qn " << tmp.quarters () - << " bbt " << tmp.bbt() - << " lock to " << tmp.lock_style() - ; + if (tmap.move_to (SECONDS_TO_SUPERCLOCK(23), SECONDS_TO_SUPERCLOCK (72))) { + tmap.rebuild (SECONDS_TO_SUPERCLOCK (120)); + tmap.dump (std::cout); + } - if (tmp.is_explicit()) { - str << " tempo " << *((Tempo*) &tmp.metric()) - << " meter " << *((Meter*) &tmp.metric()) - ; + TempoMapPoints grid; + tmap.get_grid (grid, SECONDS_TO_SUPERCLOCK (12.3), SECONDS_TO_SUPERCLOCK (44), Temporal::Beats::ticks ((Temporal::Beats::PPQN * 4) / 12)); + cout << "grid contains " << grid.size() << endl; + for (TempoMapPoints::iterator p = grid.begin(); p != grid.end(); ++p) { + cout << *p << endl; } - if (tmp.is_explicit() && tmp.ramped()) { - str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter(); + TempoMapPoints bbt_grid; + tmap.get_bar_grid (bbt_grid, SECONDS_TO_SUPERCLOCK (0), SECONDS_TO_SUPERCLOCK (100), 4); + cout << "bbt_grid contains " << bbt_grid.size() << endl; + for (TempoMapPoints::iterator p = bbt_grid.begin(); p != bbt_grid.end(); ++p) { + cout << *p << endl; } - return str; + + return 0; }